calamares/src/libcalamares/PythonJobApi.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

365 lines
11 KiB
C++
Raw Normal View History

/* === This file is part of Calamares - <https://calamares.io> ===
*
[libcalamares] Update SPDX identifiers. Update CppJob.h Update CalamaresConfig.h.in Update DllMacro.h Update GlobalStorage.cpp Update GlobalStorage.h Update Job.cpp Update Job.h Update JobExample.cpp Update JobExample.h Update JobQueue.cpp Update CalamaresConfig.h.in Update CppJob.cpp Update CppJob.h Update DllMacro.h Update GlobalStorage.cpp Update GlobalStorage.h Update Job.cpp Update Job.h Update JobExample.cpp Update JobExample.h Update JobQueue.h Update ProcessJob.cpp Update ProcessJob.h Update PythonHelper.cpp Update PythonJob.cpp Update PythonJob.h Update PythonHelper.h Update PythonJobApi.cpp Update PythonJobApi.h Update Settings.cpp Update Settings.h Update GeoIPJSON.cpp Update GeoIPJSON.h Update GeoIPTests.cpp Update GeoIPTests.h Update GeoIPXML.cpp Update GeoIPXML.h Update Handler.cpp Update Handler.h Update Interface.cpp Update Interface.h Update test_geoip.cpp Update CountryData_p.cpp Update Label.cpp Update Label.h Update LabelModel.cpp Update LabelModel.h Update CountryData_p.cpp Update CountryData_p.cpp Update Lookup.cpp Update Lookup.h Update Tests.cpp Update Tests.h Update TimeZone.cpp Update TimeZone.h Update TranslatableConfiguration.cpp Update TranslatableConfiguration.h Update ZoneData_p.cxxtr Update cldr-extractor.py Update zone-extractor.py Update Actions.h Update Actions.h Update Descriptor.h Update InstanceKey.cpp Update Module.cpp Update Module.h Update Requirement.cpp Update RequirementsChecker.h Update RequirementsModel.cpp Update RequirementsModel.h Update Tests.cpp Update Manager.cpp Update Manager.h Update Tests.cpp Update FileSystem.cpp Update FileSystem.h Update KPMManager.cpp Update KPMManager.h Update KPMTests.cpp Update FileSystem.cpp Update FileSystem.cpp Update FileSystem.h Update KPMManager.cpp Update KPMManager.h Update Mount.cpp Update Mount.h Update PartitionIterator.cpp Update PartitionIterator.h Update PartitionIterator.h Update PartitionQuery.cpp Update PartitionQuery.h Update PartitionSize.cpp Update PartitionSize.h Update Sync.cpp Update Sync.h Update Tests.cpp Update Tests.h Update BoostPython.h Update CalamaresUtilsSystem.cpp Update CalamaresUtilsSystem.h Update CommandList.cpp Update CommandList.h Update Dirs.cpp Update Dirs.h Update Entropy.cpp Update Entropy.h Update Entropy.cpp Update Logger.cpp Update Logger.h Update NamedEnum.h Update NamedSuffix.h Update PluginFactory.cpp Update PluginFactory.h Update RAII.h Update RAII.h Update Retranslator.cpp Update Retranslator.h Update String.cpp Update String.h Update TestPaths.cpp Update Tests.cpp Update Tests.h Update UMask.cpp Update UMask.h Update Units.h Update Variant.cpp Update Variant.h Update Yaml.cpp Update Yaml.h Update moc-warnings.h
2020-05-30 16:15:03 +02:00
* SPDX-FileCopyrightText: 2014-2016 Teo Mrnjavac <teo@kde.org>
* SPDX-FileCopyrightText: 2017-2020 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is Free Software: see the License-Identifier above.
[libcalamares] Update SPDX identifiers. Update CppJob.h Update CalamaresConfig.h.in Update DllMacro.h Update GlobalStorage.cpp Update GlobalStorage.h Update Job.cpp Update Job.h Update JobExample.cpp Update JobExample.h Update JobQueue.cpp Update CalamaresConfig.h.in Update CppJob.cpp Update CppJob.h Update DllMacro.h Update GlobalStorage.cpp Update GlobalStorage.h Update Job.cpp Update Job.h Update JobExample.cpp Update JobExample.h Update JobQueue.h Update ProcessJob.cpp Update ProcessJob.h Update PythonHelper.cpp Update PythonJob.cpp Update PythonJob.h Update PythonHelper.h Update PythonJobApi.cpp Update PythonJobApi.h Update Settings.cpp Update Settings.h Update GeoIPJSON.cpp Update GeoIPJSON.h Update GeoIPTests.cpp Update GeoIPTests.h Update GeoIPXML.cpp Update GeoIPXML.h Update Handler.cpp Update Handler.h Update Interface.cpp Update Interface.h Update test_geoip.cpp Update CountryData_p.cpp Update Label.cpp Update Label.h Update LabelModel.cpp Update LabelModel.h Update CountryData_p.cpp Update CountryData_p.cpp Update Lookup.cpp Update Lookup.h Update Tests.cpp Update Tests.h Update TimeZone.cpp Update TimeZone.h Update TranslatableConfiguration.cpp Update TranslatableConfiguration.h Update ZoneData_p.cxxtr Update cldr-extractor.py Update zone-extractor.py Update Actions.h Update Actions.h Update Descriptor.h Update InstanceKey.cpp Update Module.cpp Update Module.h Update Requirement.cpp Update RequirementsChecker.h Update RequirementsModel.cpp Update RequirementsModel.h Update Tests.cpp Update Manager.cpp Update Manager.h Update Tests.cpp Update FileSystem.cpp Update FileSystem.h Update KPMManager.cpp Update KPMManager.h Update KPMTests.cpp Update FileSystem.cpp Update FileSystem.cpp Update FileSystem.h Update KPMManager.cpp Update KPMManager.h Update Mount.cpp Update Mount.h Update PartitionIterator.cpp Update PartitionIterator.h Update PartitionIterator.h Update PartitionQuery.cpp Update PartitionQuery.h Update PartitionSize.cpp Update PartitionSize.h Update Sync.cpp Update Sync.h Update Tests.cpp Update Tests.h Update BoostPython.h Update CalamaresUtilsSystem.cpp Update CalamaresUtilsSystem.h Update CommandList.cpp Update CommandList.h Update Dirs.cpp Update Dirs.h Update Entropy.cpp Update Entropy.h Update Entropy.cpp Update Logger.cpp Update Logger.h Update NamedEnum.h Update NamedSuffix.h Update PluginFactory.cpp Update PluginFactory.h Update RAII.h Update RAII.h Update Retranslator.cpp Update Retranslator.h Update String.cpp Update String.h Update TestPaths.cpp Update Tests.cpp Update Tests.h Update UMask.cpp Update UMask.h Update Units.h Update Variant.cpp Update Variant.h Update Yaml.cpp Update Yaml.h Update moc-warnings.h
2020-05-30 16:15:03 +02:00
*
*/
#include "PythonJobApi.h"
#include "GlobalStorage.h"
#include "JobQueue.h"
#include "PythonHelper.h"
#include "locale/Global.h"
#include "partition/Mount.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 bp = boost::python;
static int
handle_check_target_env_call_error( const Calamares::ProcessResult& ec, const QString& cmd )
{
if ( !ec.first )
{
return ec.first;
}
QString raise = QString( "import subprocess\n"
"e = subprocess.CalledProcessError(%1,\"%2\")\n" )
.arg( ec.first )
.arg( cmd );
if ( !ec.second.isEmpty() )
{
raise.append( QStringLiteral( "e.output = \"\"\"%1\"\"\"\n" ).arg( ec.second ) );
}
raise.append( "raise e" );
bp::exec( raise.toStdString().c_str() );
bp::throw_error_already_set();
return ec.first;
}
static inline QStringList
bp_list_to_qstringlist( const bp::list& args )
{
QStringList list;
for ( int i = 0; i < bp::len( args ); ++i )
{
list.append( QString::fromStdString( bp::extract< std::string >( args[ i ] ) ) );
}
return list;
}
static inline Calamares::ProcessResult
target_env_command( const QStringList& args, const std::string& input, int timeout )
{
// Since Python doesn't give us the type system for distinguishing
// seconds from other integral types, massage to seconds here.
return Calamares::System::instance()->targetEnvCommand(
args, QString(), QString::fromStdString( input ), std::chrono::seconds( timeout ) );
}
namespace CalamaresPython
{
int
mount( const std::string& device_path,
const std::string& mount_point,
const std::string& filesystem_name,
const std::string& options )
{
return Calamares::Partition::mount( QString::fromStdString( device_path ),
QString::fromStdString( mount_point ),
QString::fromStdString( filesystem_name ),
QString::fromStdString( options ) );
}
int
target_env_call( const std::string& command, const std::string& input, int timeout )
{
return target_env_command( QStringList { QString::fromStdString( command ) }, input, timeout ).first;
}
int
target_env_call( const bp::list& args, const std::string& input, int timeout )
{
return target_env_command( bp_list_to_qstringlist( args ), input, timeout ).first;
}
int
check_target_env_call( const std::string& command, const std::string& input, int timeout )
{
auto ec = target_env_command( QStringList { QString::fromStdString( command ) }, input, timeout );
return handle_check_target_env_call_error( ec, QString::fromStdString( command ) );
}
int
check_target_env_call( const bp::list& args, const std::string& input, int timeout )
{
auto ec = target_env_command( bp_list_to_qstringlist( args ), input, timeout );
if ( !ec.first )
{
return ec.first;
}
QStringList failedCmdList = bp_list_to_qstringlist( args );
return handle_check_target_env_call_error( ec, failedCmdList.join( ' ' ) );
}
std::string
check_target_env_output( const std::string& command, const std::string& input, int timeout )
{
auto ec = target_env_command( QStringList { QString::fromStdString( command ) }, input, timeout );
handle_check_target_env_call_error( ec, QString::fromStdString( command ) );
return ec.second.toStdString();
}
std::string
check_target_env_output( const bp::list& args, const std::string& input, int timeout )
{
QStringList list = bp_list_to_qstringlist( args );
auto ec = target_env_command( list, input, timeout );
handle_check_target_env_call_error( ec, list.join( ' ' ) );
return ec.second.toStdString();
}
static const char output_prefix[] = "[PYTHON JOB]:";
static inline void
log_action( unsigned int level, const std::string& s )
{
Logger::CDebug( level ) << output_prefix << QString::fromStdString( s );
}
2014-07-28 18:55:01 +02:00
void
debug( const std::string& s )
{
log_action( Logger::LOGDEBUG, s );
2014-07-28 18:55:01 +02:00
}
void
warning( const std::string& s )
{
log_action( Logger::LOGWARNING, s );
}
void
error( const std::string& s )
{
log_action( Logger::LOGERROR, s );
}
2014-07-28 18:55:01 +02:00
boost::python::dict
load_yaml( const std::string& path )
{
const QString filePath = QString::fromStdString( path );
bool ok = false;
auto map = Calamares::YAML::load( filePath, &ok );
if ( !ok )
{
cWarning() << "Loading YAML from" << filePath << "failed.";
}
return variantMapToPyDict( map );
}
2014-07-23 12:54:53 +02:00
PythonJobInterface::PythonJobInterface( Calamares::PythonJob* parent )
: m_parent( parent )
{
auto moduleDir = QDir( m_parent->m_workingPath );
moduleName = moduleDir.dirName().toStdString();
prettyName = m_parent->prettyName().toStdString();
workingPath = m_parent->m_workingPath.toStdString();
configuration = CalamaresPython::variantMapToPyDict( m_parent->m_configurationMap );
}
2014-07-22 18:05:58 +02:00
void
2014-07-23 12:54:53 +02:00
PythonJobInterface::setprogress( qreal progress )
2014-07-22 18:05:58 +02:00
{
2020-03-05 03:40:40 +01:00
if ( progress >= 0.0 && progress <= 1.0 )
{
2014-07-22 18:05:58 +02:00
m_parent->emitProgress( progress );
}
2014-07-22 18:05:58 +02:00
}
static inline int
_process_output( Calamares::Utils::RunLocation location,
const boost::python::list& args,
const boost::python::object& callback,
const std::string& input,
int timeout )
{
Calamares::Utils::Runner r( bp_list_to_qstringlist( args ) );
r.setLocation( location );
if ( !callback.is_none() )
{
bp::extract< bp::list > x( callback );
if ( x.check() )
{
QObject::connect( &r,
&decltype( r )::output,
[ cb = callback.attr( "append" ) ]( const QString& s ) { cb( 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();
if ( result.getExitCode() )
{
return handle_check_target_env_call_error( result, r.executable() );
}
return 0;
}
int
target_env_process_output( const boost::python::list& args,
const boost::python::object& callback,
const std::string& input,
int timeout )
{
return _process_output( Calamares::Utils::RunLocation::RunInTarget, args, callback, input, timeout );
}
int
host_env_process_output( const boost::python::list& args,
const boost::python::object& callback,
const std::string& input,
int timeout )
{
return _process_output( Calamares::Utils::RunLocation::RunInHost, args, callback, input, timeout );
}
std::string
obscure( const std::string& string )
{
return Calamares::String::obscure( QString::fromStdString( string ) ).toStdString();
}
static QStringList
_gettext_languages()
{
QStringList languages;
// There are two ways that Python jobs can be initialised:
// - through JobQueue, in which case that has an instance which holds
// a GlobalStorage object, or
// - through the Python test-script, which initialises its
// own GlobalStoragePythonWrapper, which then holds a
// GlobalStorage object for all of Python.
Calamares::JobQueue* jq = Calamares::JobQueue::instance();
Calamares::GlobalStorage* gs
= jq ? jq->globalStorage() : CalamaresPython::GlobalStoragePythonWrapper::globalStorageInstance();
QString lang = Calamares::Locale::readGS( *gs, QStringLiteral( "LANG" ) );
if ( !lang.isEmpty() )
{
languages.append( lang );
if ( lang.indexOf( '.' ) > 0 )
{
lang.truncate( lang.indexOf( '.' ) );
languages.append( lang );
}
if ( lang.indexOf( '_' ) > 0 )
{
lang.truncate( lang.indexOf( '_' ) );
2017-08-21 18:36:04 +02:00
languages.append( lang );
}
}
return languages;
}
bp::list
gettext_languages()
{
bp::list pyList;
2017-08-21 18:36:04 +02:00
for ( auto lang : _gettext_languages() )
{
pyList.append( lang.toStdString() );
}
return pyList;
}
static void
2017-08-21 18:36:04 +02:00
_add_localedirs( QStringList& pathList, const QString& candidate )
{
2017-08-21 18:36:04 +02:00
if ( !candidate.isEmpty() && !pathList.contains( candidate ) )
{
2017-08-21 18:36:04 +02:00
pathList.prepend( candidate );
if ( QDir( candidate ).cd( "lang" ) )
{
2017-08-21 18:36:04 +02:00
pathList.prepend( candidate + "/lang" );
}
}
}
bp::object
gettext_path()
{
// Going to log informatively just once
static bool first_time = true;
cScopedAssignment( &first_time, false );
// TODO: distinguish between -d runs and normal runs
// TODO: can we detect DESTDIR-installs?
QStringList candidatePaths
= QStandardPaths::locateAll( QStandardPaths::GenericDataLocation, "locale", QStandardPaths::LocateDirectory );
QString extra = QCoreApplication::applicationDirPath();
_add_localedirs( candidatePaths, extra ); // Often /usr/local/bin
if ( !extra.isEmpty() )
{
2017-08-21 18:36:04 +02:00
QDir d( extra );
if ( d.cd( "../share/locale" ) ) // Often /usr/local/bin/../share/locale -> /usr/local/share/locale
{
2017-08-21 18:36:04 +02:00
_add_localedirs( candidatePaths, d.canonicalPath() );
}
}
_add_localedirs( candidatePaths, QDir().canonicalPath() ); // .
if ( first_time )
{
cDebug() << "Determining gettext path from" << candidatePaths;
}
QStringList candidateLanguages = _gettext_languages();
for ( const auto& lang : candidateLanguages )
{
2017-08-21 18:36:04 +02:00
for ( auto localedir : candidatePaths )
{
2017-08-21 18:36:04 +02:00
QDir ldir( localedir );
if ( ldir.cd( lang ) )
{
Logger::CDebug( Logger::LOGDEBUG )
<< output_prefix << "Found gettext" << lang << "in" << ldir.canonicalPath();
return bp::object( localedir.toStdString() );
}
}
}
cWarning() << "No translation found for languages" << candidateLanguages;
return bp::object(); // None
}
} // namespace CalamaresPython