This commit is contained in:
zj
2025-11-23 22:11:53 +08:00
parent a923c1098d
commit 20c7191eb6
1105 changed files with 120938 additions and 4 deletions

View File

@@ -0,0 +1,322 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2014 Teo Mrnjavac <teo@kde.org>
* SPDX-FileCopyrightText: 2017-2020, 2023 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is Free Software: see the License-Identifier above.
*
*/
#include "Api.h"
#include "Pybind11Helpers.h"
#include "PythonJob.h"
#include "GlobalStorage.h"
#include "JobQueue.h"
#include "compat/Variant.h"
#include "locale/Global.h"
#include "python/Variant.h"
#include "utils/Logger.h"
#include "utils/RAII.h"
#include "utils/Runner.h"
#include "utils/String.h"
#include "utils/System.h"
#include "utils/Yaml.h"
#include <QCoreApplication>
#include <QDir>
#include <QStandardPaths>
namespace py = pybind11;
/** @namespace
*
* Helper functions for converting Python (pybind11) types to Qt types.
*/
namespace
{
QVariantList variantListFromPyList( const Calamares::Python::List& list );
QVariantMap variantMapFromPyDict( const Calamares::Python::Dictionary& dict );
QVariant
variantFromPyObject( const py::handle& o )
{
if ( py::isinstance< Calamares::Python::Dictionary >( o ) )
{
return variantMapFromPyDict( py::cast< Calamares::Python::Dictionary >( o ) );
}
else if ( py::isinstance< Calamares::Python::List >( o ) )
{
return variantListFromPyList( py::cast< Calamares::Python::List >( o ) );
}
else if ( py::isinstance< py::int_ >( o ) )
{
return QVariant( qlonglong( py::cast< py::int_ >( o ) ) );
}
else if ( py::isinstance< py::float_ >( o ) )
{
return QVariant( double( py::cast< py::float_ >( o ) ) );
}
else if ( py::isinstance< py::str >( o ) )
{
return QVariant( QString::fromStdString( std::string( py::str( o ) ) ) );
}
else if ( py::isinstance< py::bool_ >( o ) )
{
return QVariant( bool( py::cast< py::bool_ >( o ) ) );
}
return QVariant();
}
QVariantList
variantListFromPyList( const Calamares::Python::List& list )
{
QVariantList l;
for ( const auto item : list )
{
l.append( variantFromPyObject( item ) );
}
return l;
}
QVariantMap
variantMapFromPyDict( const Calamares::Python::Dictionary& dict )
{
QVariantMap m;
for ( const auto item : dict )
{
m.insert( Calamares::Python::asQString( item.first ), variantFromPyObject( ( item.second ) ) );
}
return m;
}
QStringList
stringListFromPyList( const Calamares::Python::List& list )
{
QStringList l;
for ( const auto item : list )
{
l.append( Calamares::Python::asQString( item ) );
}
return l;
}
int
raise_on_error( const Calamares::ProcessResult& ec, const QStringList& commandList )
{
if ( ec.first == 0 )
{
return 0;
}
QString raise = QString( "import subprocess\n"
"e = subprocess.CalledProcessError(%1,\"%2\")\n" )
.arg( ec.first )
.arg( commandList.join( ' ' ) );
if ( !ec.second.isEmpty() )
{
raise.append( QStringLiteral( "e.output = \"\"\"%1\"\"\"\n" ).arg( ec.second ) );
}
raise.append( "raise e" );
py::exec( raise.toStdString() );
py::error_already_set();
return ec.first;
}
int
process_output( Calamares::Utils::RunLocation location,
const QStringList& args,
const Calamares::Python::Object& callback,
const std::string& input,
int timeout )
{
Calamares::Utils::Runner r( args );
r.setLocation( location );
if ( !callback.is_none() )
{
if ( py::isinstance< Calamares::Python::List >( callback ) )
{
QObject::connect( &r,
&decltype( r )::output,
[ list_append = callback.attr( "append" ) ]( const QString& s )
{ list_append( s.toStdString() ); } );
}
else
{
QObject::connect(
&r, &decltype( r )::output, [ &callback ]( const QString& s ) { callback( s.toStdString() ); } );
}
r.enableOutputProcessing();
}
if ( !input.empty() )
{
r.setInput( QString::fromStdString( input ) );
}
if ( timeout > 0 )
{
r.setTimeout( std::chrono::seconds( timeout ) );
}
auto result = r.run();
return raise_on_error( result, args );
}
} // namespace
/** @namespace
*
* This is where the "public Python API" lives. It does not need to
* be a namespace, and it does not need to be public, but it's
* convenient to group things together.
*/
namespace Calamares
{
namespace Python
{
int
target_env_call( const List& args, const std::string& input, int timeout )
{
return Calamares::System::instance()
->targetEnvCommand(
stringListFromPyList( args ), QString(), QString::fromStdString( input ), std::chrono::seconds( timeout ) )
.first;
}
int
target_env_call( const std::string& command, const std::string& input, int timeout )
{
return Calamares::System::instance()
->targetEnvCommand( { QString::fromStdString( command ) },
QString(),
QString::fromStdString( input ),
std::chrono::seconds( timeout ) )
.first;
}
int
check_target_env_call( const List& args, const std::string& input, int timeout )
{
const auto commandList = stringListFromPyList( args );
auto ec = Calamares::System::instance()->targetEnvCommand(
commandList, QString(), QString::fromStdString( input ), std::chrono::seconds( timeout ) );
return raise_on_error( ec, commandList );
}
std::string
check_target_env_output( const List& args, const std::string& input, int timeout )
{
const auto commandList = stringListFromPyList( args );
auto ec = Calamares::System::instance()->targetEnvCommand(
commandList, QString(), QString::fromStdString( input ), std::chrono::seconds( timeout ) );
raise_on_error( ec, commandList );
return ec.second.toStdString();
}
int
target_env_process_output( const List& args, const Object& callback, const std::string& input, int timeout )
{
return process_output(
Calamares::System::RunLocation::RunInTarget, stringListFromPyList( args ), callback, input, timeout );
}
int
host_env_process_output( const List& args, const Object& callback, const std::string& input, int timeout )
{
return process_output(
Calamares::System::RunLocation::RunInHost, stringListFromPyList( args ), callback, input, timeout );
}
JobProxy::JobProxy( Calamares::Python::Job* parent )
: prettyName( parent->prettyName().toStdString() )
, workingPath( parent->workingPath().toStdString() )
, moduleName( QDir( parent->workingPath() ).dirName().toStdString() )
, configuration( Calamares::Python::variantMapToPyDict( parent->configuration() ) )
, m_parent( parent )
{
}
void
JobProxy::setprogress( qreal progress )
{
if ( progress >= 0.0 && progress <= 1.0 )
{
m_parent->emitProgress( progress );
}
}
Calamares::GlobalStorage* GlobalStorageProxy::s_gs_instance = nullptr;
// The special handling for nullptr is only for the testing
// script for the python bindings, which passes in None;
// normal use will have a GlobalStorage from JobQueue::instance()
// passed in. Testing use will leak the allocated GlobalStorage
// object, but that's OK for testing.
GlobalStorageProxy::GlobalStorageProxy( Calamares::GlobalStorage* gs )
: m_gs( gs ? gs : s_gs_instance )
{
if ( !m_gs )
{
s_gs_instance = new Calamares::GlobalStorage;
m_gs = s_gs_instance;
}
}
bool
GlobalStorageProxy::contains( const std::string& key ) const
{
return m_gs->contains( QString::fromStdString( key ) );
}
int
GlobalStorageProxy::count() const
{
return m_gs->count();
}
void
GlobalStorageProxy::insert( const std::string& key, const Object& value )
{
m_gs->insert( QString::fromStdString( key ), variantFromPyObject( value ) );
}
List
GlobalStorageProxy::keys() const
{
List pyList;
const auto keys = m_gs->keys();
for ( const QString& key : keys )
{
pyList.append( key.toStdString() );
}
return pyList;
}
int
GlobalStorageProxy::remove( const std::string& key )
{
const QString gsKey( QString::fromStdString( key ) );
if ( !m_gs->contains( gsKey ) )
{
cWarning() << "Unknown GS key" << key.c_str();
}
return m_gs->remove( gsKey );
}
Object
GlobalStorageProxy::value( const std::string& key ) const
{
const QString gsKey( QString::fromStdString( key ) );
if ( !m_gs->contains( gsKey ) )
{
cWarning() << "Unknown GS key" << key.c_str();
return py::none();
}
return Calamares::Python::variantToPyObject( m_gs->value( gsKey ) );
}
} // namespace Python
} // namespace Calamares

View File

@@ -0,0 +1,89 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2023 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is Free Software: see the License-Identifier above.
*
*/
#ifndef CALAMARES_PYBIND11_API_H
#define CALAMARES_PYBIND11_API_H
/** @file
*
* Contains the API that Python modules use from the Python code
* of that module. This is the C++ side that implements the functions
* imported by the Python code as `import libcalamares`.
*/
#include "PythonTypes.h"
#include <string>
namespace Calamares
{
class GlobalStorage;
class PythonJob;
namespace Python __attribute__( ( visibility( "hidden" ) ) )
{
int target_env_call( const List& args, const std::string& input, int timeout );
int target_env_call( const std::string& command, const std::string& input, int timeout );
int check_target_env_call( const List& args, const std::string& input, int timeout );
std::string check_target_env_output( const List& args, const std::string& input, int timeout );
int target_env_process_output( const List& args, const Object& callback, const std::string& input, int timeout );
int host_env_process_output( const List& args, const Object& callback, const std::string& input, int timeout );
class Job;
/** @brief Proxy class in Python for the Calamares Job class
*
* This is available as libcalamares.job in Python code.
*/
class JobProxy
{
public:
explicit JobProxy( Calamares::Python::Job* parent );
std::string prettyName;
std::string workingPath;
std::string moduleName;
Dictionary configuration;
void setprogress( qreal progress );
private:
Calamares::Python::Job* m_parent;
};
class GlobalStorageProxy
{
public:
explicit GlobalStorageProxy( Calamares::GlobalStorage* gs );
bool contains( const std::string& key ) const;
int count() const;
void insert( const std::string& key, const Object& value );
List keys() const;
int remove( const std::string& key );
Object value( const std::string& key ) const;
// This is a helper for scripts that do not go through
// the JobQueue (i.e. the module testpython script),
// which allocate their own (singleton) GlobalStorage.
static Calamares::GlobalStorage* globalStorageInstance() { return s_gs_instance; }
private:
Calamares::GlobalStorage* m_gs;
static Calamares::GlobalStorage* s_gs_instance; // See globalStorageInstance()
};
} // namespace Python
} // namespace Calamares
#endif

View File

@@ -0,0 +1,30 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2023 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is Free Software: see the License-Identifier above.
*
*/
#ifndef CALAMARES_PYBIND11_PYBIND11HELPERS_H
#define CALAMARES_PYBIND11_PYBIND11HELPERS_H
#include "PythonTypes.h"
#include <QString>
namespace Calamares
{
namespace Python __attribute__( ( visibility( "hidden" ) ) )
{
inline QString asQString( const pybind11::handle& o )
{
return QString::fromUtf8( pybind11::str( o ).cast< std::string >().c_str() );
}
} // namespace Python
} // namespace Calamares
#endif

View File

@@ -0,0 +1,434 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2023 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is Free Software: see the License-Identifier above.
*
*/
#include "PythonJob.h"
#include "CalamaresVersionX.h"
#include "GlobalStorage.h"
#include "JobQueue.h"
#include "pybind11/Api.h"
#include "pybind11/Pybind11Helpers.h"
#include "python/Api.h"
#include "utils/Logger.h"
#include <QDir>
#include <QFileInfo>
#include <QString>
#ifdef WITH_PYBIND11
#else
#error Source only for pybind11
#endif
namespace py = pybind11;
// Forward-declare function generated by PYBIND11_MODULE
static void pybind11_init_libcalamares( ::pybind11::module_& variable );
namespace
{
static const char* s_preScript = nullptr;
QString
getPrettyNameFromScope( const py::dict& scope )
{
static constexpr char key_name[] = "pretty_name";
if ( scope.contains( key_name ) )
{
const py::object func = scope[ key_name ];
try
{
const auto s = func().cast< std::string >();
return QString::fromUtf8( s.c_str() );
}
catch ( const py::cast_error& )
{
// Ignore, we will try __doc__ next
}
}
static constexpr char key_doc[] = "__doc__";
if ( scope.contains( key_doc ) )
{
const py::object doc = scope[ key_doc ];
try
{
const auto s = doc.cast< std::string >();
auto string = QString::fromUtf8( s.c_str() ).trimmed();
const auto newline_index = string.indexOf( '\n' );
if ( newline_index >= 0 )
{
string.truncate( newline_index );
return string;
}
// __doc__ is apparently empty, try next fallback
}
catch ( const py::cast_error& )
{
// Ignore, try next fallback
}
}
// No more fallbacks
return QString();
}
void
populate_utils( py::module_& m )
{
m.def( "obscure", &Calamares::Python::obscure, "A function that obscures (encodes) a string" );
m.def( "debug", &Calamares::Python::debug, "Log a debug-message" );
m.def( "warn", &Calamares::Python::warning, "Log a warning-message" );
m.def( "warning", &Calamares::Python::warning, "Log a warning-message" );
m.def( "error", &Calamares::Python::error, "Log an error-message" );
m.def( "load_yaml", &Calamares::Python::load_yaml, "Loads YAML from a file." );
m.def( "target_env_call",
py::overload_cast<const Calamares::Python::List& , const std::string& , int >(&Calamares::Python::target_env_call),
"Runs command_list in target, returns exit code.",
py::arg( "command_list" ),
py::arg( "input" ) = std::string(),
py::arg( "timeout" ) = 0 );
m.def( "target_env_call",
py::overload_cast<const std::string& , const std::string& , int >(&Calamares::Python::target_env_call),
"Runs command in target, returns exit code.",
py::arg( "command_list" ),
py::arg( "input" ) = std::string(),
py::arg( "timeout" ) = 0 );
m.def( "check_target_env_call",
&Calamares::Python::check_target_env_call,
"Runs command in target, raises on error exit.",
py::arg( "command_list" ),
py::arg( "input" ) = std::string(),
py::arg( "timeout" ) = 0 );
m.def( "check_target_env_output",
&Calamares::Python::check_target_env_output,
"Runs command in target, returns standard output or raises on error.",
py::arg( "command_list" ),
py::arg( "input" ) = std::string(),
py::arg( "timeout" ) = 0 );
m.def( "target_env_process_output",
&Calamares::Python::target_env_process_output,
"Runs command in target, updating callback and returns standard output or raises on error.",
py::arg( "command_list" ),
py::arg( "callback" ) = pybind11::none(),
py::arg( "input" ) = std::string(),
py::arg( "timeout" ) = 0 );
m.def( "host_env_process_output",
&Calamares::Python::host_env_process_output,
"Runs command in target, updating callback and returns standard output or raises on error.",
py::arg( "command_list" ),
py::arg( "callback" ) = pybind11::none(),
py::arg( "input" ) = std::string(),
py::arg( "timeout" ) = 0 );
m.def( "gettext_languages",
&Calamares::Python::gettext_languages,
"Returns list of languages (most to least-specific) for gettext." );
m.def( "gettext_path", &Calamares::Python::gettext_path, "Returns path for gettext search." );
m.def( "mount",
&Calamares::Python::mount,
"Runs the mount utility with the specified parameters.\n"
"Returns the program's exit code, or:\n"
"-1 = QProcess crash\n"
"-2 = QProcess cannot start\n"
"-3 = bad arguments" );
}
void
populate_libcalamares( py::module_& m )
{
m.doc() = "Calamares API for Python";
m.add_object( "ORGANIZATION_NAME", Calamares::Python::String( CALAMARES_ORGANIZATION_NAME ) );
m.add_object( "ORGANIZATION_DOMAIN", Calamares::Python::String( CALAMARES_ORGANIZATION_DOMAIN ) );
m.add_object( "APPLICATION_NAME", Calamares::Python::String( CALAMARES_APPLICATION_NAME ) );
m.add_object( "VERSION", Calamares::Python::String( CALAMARES_VERSION ) );
m.add_object( "VERSION_SHORT", Calamares::Python::String( CALAMARES_VERSION_SHORT ) );
auto utils = m.def_submodule( "utils", "Calamares Utility API for Python" );
populate_utils( utils );
py::class_< Calamares::Python::JobProxy >( m, "Job" )
.def_readonly( "module_name", &Calamares::Python::JobProxy::moduleName )
.def_readonly( "pretty_name", &Calamares::Python::JobProxy::prettyName )
.def_readonly( "working_path", &Calamares::Python::JobProxy::workingPath )
.def_readonly( "configuration", &Calamares::Python::JobProxy::configuration )
.def( "setprogress", &Calamares::Python::JobProxy::setprogress );
py::class_< Calamares::Python::GlobalStorageProxy >( m, "GlobalStorage" )
.def( py::init( []( std::nullptr_t ) { return new Calamares::Python::GlobalStorageProxy( nullptr ); } ) )
.def( "contains", &Calamares::Python::GlobalStorageProxy::contains )
.def( "count", &Calamares::Python::GlobalStorageProxy::count )
.def( "insert", &Calamares::Python::GlobalStorageProxy::insert )
.def( "keys", &Calamares::Python::GlobalStorageProxy::keys )
.def( "remove", &Calamares::Python::GlobalStorageProxy::remove )
.def( "value", &Calamares::Python::GlobalStorageProxy::value );
}
} // namespace
namespace Calamares
{
namespace Python
{
struct Job::Private
{
Private( const QString& script, const QString& path, const QVariantMap& configuration )
: scriptFile( script )
, workingPath( path )
, configurationMap( configuration )
{
}
QString scriptFile; // From the module descriptor
QString workingPath;
QVariantMap configurationMap; // The module configuration
QString description; // Obtained from the Python code
};
Job::Job( const QString& scriptFile,
const QString& workingPath,
const QVariantMap& moduleConfiguration,
QObject* parent )
: ::Calamares::Job( parent )
, m_d( std::make_unique< Job::Private >( scriptFile, workingPath, moduleConfiguration ) )
{
}
Job::~Job() {}
QString
Job::prettyName() const
{
return QDir( m_d->workingPath ).dirName();
}
QString
Job::prettyStatusMessage() const
{
// The description is updated when progress is reported, see emitProgress()
if ( m_d->description.isEmpty() )
{
return tr( "Running %1 operation." ).arg( prettyName() );
}
else
{
return m_d->description;
}
}
JobResult
Job::exec()
{
// We assume m_scriptFile to be relative to m_workingPath.
QDir workingDir( m_d->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_d->workingPath )
.arg( prettyName() ) );
}
QFileInfo scriptFI( workingDir.absoluteFilePath( m_d->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() ) );
}
py::scoped_interpreter guard {};
// Import, but do not keep the handle lying around
try
{
// import() only works if the library can be found through
// normal Python import mechanisms -- and after installation,
// libcalamares can not be found. An alternative, like using
// PYBIND11_EMBEDDED_MODULE, falls foul of not being able
// to `import libcalamares` from external Python scripts,
// which are used in tests.
//
// auto calamaresModule = py::module_::import( "libcalamares" );
//
// Using the constructor directly generates compiler warnings
// because this is deprecated.
//
// auto calamaresModule = py::module_("libcalamares");
//
// So create it by hand, using code cribbed from pybind11/embed.h
// to register an extension module. This does not make it
// available to the current interpreter.
//
static ::pybind11::module_::module_def libcalamares_def;
auto calamaresModule = py::module_::create_extension_module( "libcalamares", nullptr, &libcalamares_def );
pybind11_init_libcalamares( calamaresModule );
// Add libcalamares to the main namespace (as if it has already
// been imported) and also to sys.modules under its own name.
// Now `import libcalamares` in modules will find the already-
// loaded module.
auto scope = py::module_::import( "__main__" ).attr( "__dict__" );
scope[ "libcalamares" ] = calamaresModule;
auto sys = scope[ "sys" ].attr( "modules" );
sys[ "libcalamares" ] = calamaresModule;
calamaresModule.attr( "job" ) = Calamares::Python::JobProxy( this );
calamaresModule.attr( "globalstorage" )
= Calamares::Python::GlobalStorageProxy( JobQueue::instance()->globalStorage() );
}
catch ( const py::error_already_set& e )
{
cError() << "Error in import:" << e.what();
throw; // This is non-recoverable
}
if ( s_preScript )
{
try
{
py::exec( s_preScript );
}
catch ( const py::error_already_set& e )
{
cError() << "Error in pre-script:" << e.what();
return JobResult::internalError(
tr( "Bad internal script" ),
tr( "Internal script for python job %1 raised an exception." ).arg( prettyName() ),
JobResult::PythonUncaughtException );
}
}
try
{
py::eval_file( scriptFI.absoluteFilePath().toUtf8().constData() );
}
catch ( const py::error_already_set& e )
{
cError() << "Error while loading:" << e.what();
return JobResult::internalError(
tr( "Bad main script file" ),
tr( "Main script file %1 for python job %2 could not be loaded because it raised an exception." )
.arg( scriptFI.absoluteFilePath() )
.arg( prettyName() ),
JobResult::PythonUncaughtException );
}
auto scope = py::module_::import( "__main__" ).attr( "__dict__" );
m_d->description = getPrettyNameFromScope( scope );
Q_EMIT progress( 0 );
static constexpr char key_run[] = "run";
if ( scope.contains( key_run ) )
{
const py::object run = scope[ key_run ];
try
{
py::object r;
try
{
r = run();
}
catch ( const py::error_already_set& e )
{
// This is an error in the Python code itself
cError() << "Error while running:" << e.what();
return JobResult::internalError( tr( "Bad main script file" ),
tr( "Main script file %1 for python job %2 raised an exception." )
.arg( scriptFI.absoluteFilePath() )
.arg( prettyName() ),
JobResult::PythonUncaughtException );
}
if ( r.is( py::none() ) )
{
return JobResult::ok();
}
const py::tuple items = r;
return JobResult::error( asQString( items[ 0 ] ), asQString( items[ 1 ] ) );
}
catch ( const py::cast_error& e )
{
cError() << "Error in type of run() or its results:" << e.what();
return JobResult::error( tr( "Bad main script file" ),
tr( "Main script file %1 for python job %2 returned invalid results." )
.arg( scriptFI.absoluteFilePath() )
.arg( prettyName() ) );
}
catch ( const py::error_already_set& e )
{
cError() << "Error in return type of run():" << e.what();
return JobResult::error( tr( "Bad main script file" ),
tr( "Main script file %1 for python job %2 returned invalid results." )
.arg( scriptFI.absoluteFilePath() )
.arg( prettyName() ) );
}
}
else
{
return JobResult::error( tr( "Bad main script file" ),
tr( "Main script file %1 for python job %2 does not contain a run() function." )
.arg( scriptFI.absoluteFilePath() )
.arg( prettyName() ) );
}
}
QString
Job::workingPath() const
{
return m_d->workingPath;
}
QVariantMap
Job::configuration() const
{
return m_d->configurationMap;
}
void
Job::emitProgress( double progressValue )
{
// TODO: update prettyname
emit progress( progressValue );
}
/** @brief Sets the pre-run Python code for all PythonJobs
*
* A PythonJob runs the code from the scriptFile parameter to
* the constructor; the pre-run code is **also** run, before
* even the scriptFile code. Use this in testing mode
* to modify Python internals.
*
* No ownership of @p script is taken: pass in a pointer to
* a character literal or something that lives longer than the
* job. Pass in @c nullptr to switch off pre-run code.
*/
void
Job::setInjectedPreScript( const char* script )
{
s_preScript = script;
cDebug() << "Python pre-script set to string" << Logger::Pointer( script ) << "length"
<< ( script ? strlen( script ) : 0 );
}
} // namespace Python
} // namespace Calamares
PYBIND11_MODULE( libcalamares, m )
{
populate_libcalamares( m );
}

View File

@@ -0,0 +1,73 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2023 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is Free Software: see the License-Identifier above.
*
*/
#ifndef CALAMARES_PYBIND11_PYTHONJOB_H
#define CALAMARES_PYBIND11_PYTHONJOB_H
// This file is called PythonJob.h because it would otherwise
// clash with the Job.h in libcalamares proper.
#include "CalamaresConfig.h"
#include "DllMacro.h"
#include "Job.h"
#include <QVariantMap>
#include <memory>
#ifdef WITH_PYBIND11
#else
#error Source only for pybind11
#endif
namespace Calamares
{
namespace Python
{
class Job : public ::Calamares::Job
{
Q_OBJECT
public:
explicit DLLEXPORT Job( const QString& scriptFile,
const QString& workingPath,
const QVariantMap& moduleConfiguration = QVariantMap(),
QObject* parent = nullptr );
~Job() override;
QString prettyName() const override;
QString prettyStatusMessage() const override;
::Calamares::JobResult exec() override;
/** @brief Sets the pre-run Python code for all PythonJobs
*
* A PythonJob runs the code from the scriptFile parameter to
* the constructor; the pre-run code is **also** run, before
* even the scriptFile code. Use this in testing mode
* to modify Python internals.
*
* No ownership of @p script is taken: pass in a pointer to
* a character literal or something that lives longer than the
* job. Pass in @c nullptr to switch off pre-run code.
*/
static DLLEXPORT void setInjectedPreScript( const char* script );
/** @brief Accessors for JobProxy */
QString workingPath() const;
QVariantMap configuration() const;
/** @brief Proxy functions */
void emitProgress( double progressValue );
private:
struct Private;
std::unique_ptr< Private > m_d;
};
} // namespace Python
} // namespace Calamares
#endif

View File

@@ -0,0 +1,56 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2023, 2024 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is Free Software: see the License-Identifier above.
*
*/
#ifndef CALAMARES_PYBIND11_PYTHONTYPES_H
#define CALAMARES_PYBIND11_PYTHONTYPES_H
#include <QString>
QT_WARNING_PUSH
QT_WARNING_DISABLE_CLANG( "-Wcovered-switch-default" )
QT_WARNING_DISABLE_CLANG( "-Wfloat-equal" )
QT_WARNING_DISABLE_CLANG( "-Wweak-vtables" )
QT_WARNING_DISABLE_CLANG( "-Wmissing-variable-declarations" )
QT_WARNING_DISABLE_CLANG( "-Wold-style-cast" )
QT_WARNING_DISABLE_CLANG( "-Wshadow-uncaptured-local" )
QT_WARNING_DISABLE_CLANG( "-Wshadow-field-in-constructor" )
QT_WARNING_DISABLE_CLANG( "-Wshadow-field" )
QT_WARNING_DISABLE_CLANG( "-Wdocumentation" )
QT_WARNING_DISABLE_CLANG( "-Wmissing-noreturn" )
QT_WARNING_DISABLE_CLANG( "-Wreserved-identifier" )
#undef slots
#include <pybind11/pybind11.h>
#include <pybind11/embed.h>
#include <pybind11/eval.h>
QT_WARNING_POP
namespace Calamares
{
namespace Python __attribute__( ( visibility( "hidden" ) ) )
{
using Dictionary = pybind11::dict;
using List = pybind11::list;
using Object = pybind11::object;
inline auto None()
{
return pybind11::none();
}
using Integer = pybind11::int_;
using Float = pybind11::float_;
using Boolean = pybind11::bool_;
using String = pybind11::str;
} // namespace Python
} // namespace Calamares
#endif