From b924aeef2b5123cac89e17308d0e45dcbd942d49 Mon Sep 17 00:00:00 2001 From: Teo Mrnjavac Date: Wed, 16 Jul 2014 16:07:32 +0200 Subject: [PATCH] Add optional Python jobs support to libcalamares. --- src/libcalamares/CMakeLists.txt | 26 +++++- src/libcalamares/CalamaresConfig.h.in | 1 + src/libcalamares/JobQueue.cpp | 8 ++ src/libcalamares/PythonJob.cpp | 113 ++++++++++++++++++++++++++ src/libcalamares/PythonJob.h | 49 +++++++++++ src/libcalamares/PythonJobHelper.cpp | 67 +++++++++++++++ src/libcalamares/PythonJobHelper.h | 48 +++++++++++ 7 files changed, 311 insertions(+), 1 deletion(-) create mode 100644 src/libcalamares/PythonJob.cpp create mode 100644 src/libcalamares/PythonJob.h create mode 100644 src/libcalamares/PythonJobHelper.cpp create mode 100644 src/libcalamares/PythonJobHelper.h diff --git a/src/libcalamares/CMakeLists.txt b/src/libcalamares/CMakeLists.txt index 9845d5b76..07ca6c12f 100644 --- a/src/libcalamares/CMakeLists.txt +++ b/src/libcalamares/CMakeLists.txt @@ -31,6 +31,28 @@ include_directories( ${QT_INCLUDE_DIR} ) + +if( WITH_PYTHON ) + set( libSources + ${libSources} + PythonJob.cpp + PythonJobHelper.cpp + ) + + include_directories(${PYTHON_INCLUDE_DIRS}) + link_directories(${PYTHON_LIBRARIES}) + + include_directories(${Boost_INCLUDE_DIRS}) + link_directories(${Boost_LIBRARY_DIRS}) + + set( OPTIONAL_PRIVATE_LIBRARIES + ${OPTIONAL_PRIVATE_LIBRARIES} + ${PYTHON_LIBRARIES} + ${Boost_LIBRARIES} + ) +endif() + + add_library( calamareslib SHARED ${libSources} ) set_target_properties( calamareslib PROPERTIES @@ -42,9 +64,11 @@ set_target_properties( calamareslib qt5_use_modules( calamareslib Core ) + target_link_libraries( calamareslib - #LINK_PRIVATE + LINK_PRIVATE # internal deps, if any + ${OPTIONAL_PRIVATE_LIBRARIES} LINK_PUBLIC # External deps diff --git a/src/libcalamares/CalamaresConfig.h.in b/src/libcalamares/CalamaresConfig.h.in index 2126124a6..7d5ce35d1 100644 --- a/src/libcalamares/CalamaresConfig.h.in +++ b/src/libcalamares/CalamaresConfig.h.in @@ -8,5 +8,6 @@ #define CMAKE_INSTALL_FULL_DATADIR "${CMAKE_INSTALL_FULL_DATADIR}/calamares" //cmakedefines for CMake variables (e.g. for optdepends) go here +#cmakedefine WITH_PYTHON #endif // CALAMARESCONFIG_H diff --git a/src/libcalamares/JobQueue.cpp b/src/libcalamares/JobQueue.cpp index edd437d84..e7b5e2398 100644 --- a/src/libcalamares/JobQueue.cpp +++ b/src/libcalamares/JobQueue.cpp @@ -20,6 +20,11 @@ #include "Job.h" +#include "CalamaresConfig.h" +#ifdef WITH_PYTHON +#include "PythonJobHelper.h" +#endif + #include namespace Calamares @@ -32,6 +37,9 @@ public: : QThread( queue ) , m_queue( queue ) { +#ifdef WITH_PYTHON + new PythonJobHelper( this ); +#endif } void setJobs( const QList< Calamares::job_ptr >& jobs ) diff --git a/src/libcalamares/PythonJob.cpp b/src/libcalamares/PythonJob.cpp new file mode 100644 index 000000000..0ede795ea --- /dev/null +++ b/src/libcalamares/PythonJob.cpp @@ -0,0 +1,113 @@ +/* === This file is part of Calamares - === + * + * Copyright 2014, Teo Mrnjavac + * + * Calamares is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Calamares is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Calamares. If not, see . + */ + +#include "PythonJob.h" + +#include "PythonJobHelper.h" +#include "utils/Logger.h" + +#include + +#undef slots +#include + + +namespace bp = boost::python; +namespace Calamares { + + +PythonJob::PythonJob( const QString& scriptFile, + const QString& workingPath, + QObject* parent ) + : Job( parent ) + , m_scriptFile( scriptFile ) + , m_workingPath( workingPath ) +{ +} + + +PythonJob::~PythonJob() +{} + + +QString +PythonJob::prettyName() const +{ + return tr( "Run script %1" ) + .arg( QDir( m_workingPath ).dirName() + + QDir::separator() + + m_scriptFile ); +} + + +JobResult +PythonJob::exec() +{ + // We assume m_scriptFile to be relative to m_workingPath. + QDir workingDir( m_workingPath ); + if ( !workingDir.exists() || + !workingDir.isReadable() ) + { + return JobResult::error( tr( "Bad working directory path" ), + tr( "Working directory %1 for python job %2 is not readable." ) + .arg( m_workingPath ) + .arg( prettyName() ) ); + } + + QFileInfo scriptFI( workingDir.absoluteFilePath( m_scriptFile ) ); + if ( !scriptFI.exists() || + !scriptFI.isFile() || + !scriptFI.isReadable() ) + { + return JobResult::error( tr( "Bad main script file" ), + tr( "Main script file %1 for python job %2 is not readable." ) + .arg( scriptFI.absoluteFilePath() ) + .arg( prettyName() ) ); + } + + try + { + bp::object scriptNamespace = helper()->createCleanNamespace(); + + bp::object result = bp::exec_file( scriptFI.absoluteFilePath().toLocal8Bit().data(), + scriptNamespace, + scriptNamespace ); + + bp::object entryPoint = scriptNamespace[ "calamares_main" ]; + + QString message = QString::fromStdString( bp::extract< std::string >( entryPoint() ) ); + + cDebug() << "Python job" << prettyName() << "finished with message" << message; + } + catch ( bp::error_already_set e ) + { + return JobResult::error( tr( "Boost.Python error" ) ); + } + + return JobResult::ok(); +} + + +PythonJobHelper* +PythonJob::helper() +{ + return PythonJobHelper::s_instance; +} + + +} // namespace Calamares diff --git a/src/libcalamares/PythonJob.h b/src/libcalamares/PythonJob.h new file mode 100644 index 000000000..f4f437697 --- /dev/null +++ b/src/libcalamares/PythonJob.h @@ -0,0 +1,49 @@ +/* === This file is part of Calamares - === + * + * Copyright 2014, Teo Mrnjavac + * + * Calamares is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Calamares is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Calamares. If not, see . + */ + +#ifndef CALAMARES_PYTHONJOB_H +#define CALAMARES_PYTHONJOB_H + +#include "Job.h" + +namespace Calamares { + +class PythonJobHelper; + +class PythonJob : public Job +{ + Q_OBJECT +public: + explicit PythonJob( const QString& scriptFile, + const QString& workingPath, + QObject* parent = nullptr ); + virtual ~PythonJob(); + + QString prettyName() const override; + JobResult exec() override; + +private: + friend class PythonJobHelper; + PythonJobHelper* helper(); + QString m_scriptFile; + QString m_workingPath; +}; + +} // namespace Calamares + +#endif // CALAMARES_PYTHONJOB_H diff --git a/src/libcalamares/PythonJobHelper.cpp b/src/libcalamares/PythonJobHelper.cpp new file mode 100644 index 000000000..98521b85a --- /dev/null +++ b/src/libcalamares/PythonJobHelper.cpp @@ -0,0 +1,67 @@ +/* === This file is part of Calamares - === + * + * Copyright 2014, Teo Mrnjavac + * + * Calamares is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Calamares is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Calamares. If not, see . + */ + +#include "PythonJobHelper.h" + +#include "utils/Logger.h" + +#include + +namespace bp = boost::python; + +namespace Calamares { + +PythonJobHelper* PythonJobHelper::s_instance = nullptr; + +PythonJobHelper::PythonJobHelper( QObject* parent ) + : QObject( parent ) +{ + // Let's make extra sure we only call Py_Initialize once + if ( !s_instance ) + { + Py_Initialize(); + + m_mainModule = bp::import( "__main__" ); + m_mainNamespace = m_mainModule.attr( "__dict__" ); + } + else + { + cDebug() << "WARNING: creating PythonJobHelper more than once. This is very bad."; + return; + } + + s_instance = this; +} + +PythonJobHelper::~PythonJobHelper() +{} + + +boost::python::object +PythonJobHelper::createCleanNamespace() +{ + // To make sure we run each script with a clean namespace, we only fetch the + // builtin namespace from the interpreter as it was when freshly initialized. + bp::dict scriptNamespace; + scriptNamespace[ "__builtins__" ] = m_mainNamespace[ "__builtins__" ]; + + return scriptNamespace; +} + + +} // namespace Calamares diff --git a/src/libcalamares/PythonJobHelper.h b/src/libcalamares/PythonJobHelper.h new file mode 100644 index 000000000..277bca10a --- /dev/null +++ b/src/libcalamares/PythonJobHelper.h @@ -0,0 +1,48 @@ +/* === This file is part of Calamares - === + * + * Copyright 2014, Teo Mrnjavac + * + * Calamares is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Calamares is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Calamares. If not, see . + */ + +#ifndef CALAMARES_PYTHONJOBHELPER_H +#define CALAMARES_PYTHONJOBHELPER_H + +#include "PythonJob.h" + +#undef slots +#include + +namespace Calamares { + +class PythonJobHelper : public QObject +{ + Q_OBJECT +public: + explicit PythonJobHelper( QObject* parent = nullptr ); + virtual ~PythonJobHelper(); + + boost::python::object createCleanNamespace(); + +private: + friend PythonJobHelper* PythonJob::helper(); + static PythonJobHelper* s_instance; + + boost::python::object m_mainModule; + boost::python::object m_mainNamespace; +}; + +} // namespace Calamares + +#endif // CALAMARES_PYTHONJOBHELPER_H