2022-05-13 13:12:05 +00:00
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
2016-12-30 15:11:20 +00:00
# include <QCoreApplication>
# include <QStringList>
# include <QCommandLineParser>
# include <QFile>
# include <QFileInfo>
# include <QDateTime>
2017-03-14 18:26:14 +00:00
# include <QHashFunctions>
2018-01-22 15:46:03 +00:00
# include <QSaveFile>
2018-08-16 11:06:22 +00:00
# include <QScopedPointer>
# include <QScopeGuard>
2021-11-23 18:00:29 +00:00
# include <QLibraryInfo>
# include <QLoggingCategory>
2016-12-30 15:11:20 +00:00
# include <private/qqmlirbuilder_p.h>
2024-05-16 13:53:23 +00:00
# include <private/qqmljscompiler_p.h>
2018-02-12 13:03:13 +00:00
# include <private/qqmljslexer_p.h>
2020-10-20 13:07:59 +00:00
# include <private/qqmljsloadergenerator_p.h>
2024-05-16 13:53:23 +00:00
# include <private/qqmljsparser_p.h>
# include <private/qqmljsresourcefilemapper_p.h>
# include <private/qqmljsutils_p.h>
2020-10-20 11:57:38 +00:00
# include <private/qresourcerelocater_p.h>
2018-01-22 15:46:03 +00:00
2019-01-02 12:08:40 +00:00
# include <algorithm>
2023-07-11 13:01:10 +00:00
using namespace Qt : : Literals : : StringLiterals ;
2019-07-30 14:36:53 +00:00
static bool argumentsFromCommandLineAndFile ( QStringList & allArguments , const QStringList & arguments )
{
allArguments . reserve ( arguments . size ( ) ) ;
for ( const QString & argument : arguments ) {
// "@file" doesn't start with a '-' so we can't use QCommandLineParser for it
2023-07-11 13:01:10 +00:00
if ( argument . startsWith ( u ' @ ' ) ) {
2019-07-30 14:36:53 +00:00
QString optionsFile = argument ;
optionsFile . remove ( 0 , 1 ) ;
if ( optionsFile . isEmpty ( ) ) {
fprintf ( stderr , " The @ option requires an input file " ) ;
return false ;
}
QFile f ( optionsFile ) ;
if ( ! f . open ( QIODevice : : ReadOnly | QIODevice : : Text ) ) {
fprintf ( stderr , " Cannot open options file specified with @ " ) ;
return false ;
}
while ( ! f . atEnd ( ) ) {
QString line = QString : : fromLocal8Bit ( f . readLine ( ) . trimmed ( ) ) ;
if ( ! line . isEmpty ( ) )
allArguments < < line ;
}
} else {
allArguments < < argument ;
}
}
return true ;
}
2016-12-30 15:11:20 +00:00
int main ( int argc , char * * argv )
{
// Produce reliably the same output for the same input by disabling QHash's random seeding.
2022-08-26 15:40:24 +00:00
QHashSeed : : setDeterministicGlobalSeed ( ) ;
2016-12-30 15:11:20 +00:00
QCoreApplication app ( argc , argv ) ;
2023-07-11 13:01:10 +00:00
QCoreApplication : : setApplicationName ( " qmlcachegen " _L1 ) ;
2016-12-30 15:11:20 +00:00
QCoreApplication : : setApplicationVersion ( QLatin1String ( QT_VERSION_STR ) ) ;
QCommandLineParser parser ;
parser . addHelpOption ( ) ;
parser . addVersionOption ( ) ;
2023-07-11 13:01:10 +00:00
QCommandLineOption bareOption ( " bare " _L1 , QCoreApplication : : translate ( " main " , " Do not include default import directories. This may be used to run qmlcachegen on a project using a different Qt version. " ) ) ;
2022-09-08 14:18:34 +00:00
parser . addOption ( bareOption ) ;
2023-07-11 13:01:10 +00:00
QCommandLineOption filterResourceFileOption ( " filter-resource-file " _L1 , QCoreApplication : : translate ( " main " , " Filter out QML/JS files from a resource file that can be cached ahead of time instead " ) ) ;
2018-01-22 15:46:03 +00:00
parser . addOption ( filterResourceFileOption ) ;
2023-07-11 13:01:10 +00:00
QCommandLineOption resourceFileMappingOption ( " resource-file-mapping " _L1 , QCoreApplication : : translate ( " main " , " Path from original resource file to new one " ) , QCoreApplication : : translate ( " main " , " old-name=new-name " ) ) ;
2018-01-22 15:46:03 +00:00
parser . addOption ( resourceFileMappingOption ) ;
2023-07-11 13:01:10 +00:00
QCommandLineOption resourceOption ( " resource " _L1 , QCoreApplication : : translate ( " main " , " Qt resource file that might later contain one of the compiled files " ) , QCoreApplication : : translate ( " main " , " resource-file-name " ) ) ;
2018-01-22 15:46:03 +00:00
parser . addOption ( resourceOption ) ;
2023-07-11 13:01:10 +00:00
QCommandLineOption resourcePathOption ( " resource-path " _L1 , QCoreApplication : : translate ( " main " , " Qt resource file path corresponding to the file being compiled " ) , QCoreApplication : : translate ( " main " , " resource-path " ) ) ;
2018-01-22 15:46:03 +00:00
parser . addOption ( resourcePathOption ) ;
2023-07-11 13:01:10 +00:00
QCommandLineOption resourceNameOption ( " resource-name " _L1 , QCoreApplication : : translate ( " main " , " Required to generate qmlcache_loader without qrc files. This is the name of the Qt resource the input files belong to. " ) , QCoreApplication : : translate ( " main " , " compiled-file-list " ) ) ;
2019-07-30 14:36:53 +00:00
parser . addOption ( resourceNameOption ) ;
2023-07-11 13:01:10 +00:00
QCommandLineOption directCallsOption ( " direct-calls " _L1 , QCoreApplication : : translate ( " main " , " This option is ignored. " ) ) ;
2021-01-05 16:49:30 +00:00
directCallsOption . setFlags ( QCommandLineOption : : HiddenFromHelp ) ;
2020-11-30 14:28:19 +00:00
parser . addOption ( directCallsOption ) ;
2024-04-18 09:05:44 +00:00
QCommandLineOption staticOption ( " static " _L1 , QCoreApplication : : translate ( " main " , " This option is ignored. " ) ) ;
staticOption . setFlags ( QCommandLineOption : : HiddenFromHelp ) ;
parser . addOption ( staticOption ) ;
2023-07-11 13:01:10 +00:00
QCommandLineOption importsOption ( " i " _L1 , QCoreApplication : : translate ( " main " , " Import extra qmldir " ) , QCoreApplication : : translate ( " main " , " qmldir file " ) ) ;
2021-11-23 18:00:29 +00:00
parser . addOption ( importsOption ) ;
2023-07-11 13:01:10 +00:00
QCommandLineOption importPathOption ( " I " _L1 , QCoreApplication : : translate ( " main " , " Look for QML modules in specified directory " ) , QCoreApplication : : translate ( " main " , " import directory " ) ) ;
2021-03-01 13:12:45 +00:00
parser . addOption ( importPathOption ) ;
2023-07-11 13:01:10 +00:00
QCommandLineOption onlyBytecode ( " only-bytecode " _L1 , QCoreApplication : : translate ( " main " , " Generate only byte code for bindings and functions, no C++ code " ) ) ;
2021-11-23 18:00:29 +00:00
parser . addOption ( onlyBytecode ) ;
2023-07-11 13:01:10 +00:00
QCommandLineOption verboseOption ( " verbose " _L1 , QCoreApplication : : translate ( " main " , " Output compile warnings " ) ) ;
2023-04-20 14:48:17 +00:00
parser . addOption ( verboseOption ) ;
2023-12-20 12:50:19 +00:00
QCommandLineOption warningsAreErrorsOption ( " warnings-are-errors " _L1 , QCoreApplication : : translate ( " main " , " Treat warnings as errors " ) ) ;
parser . addOption ( warningsAreErrorsOption ) ;
2023-07-11 13:01:10 +00:00
QCommandLineOption validateBasicBlocksOption ( " validate-basic-blocks " _L1 , QCoreApplication : : translate ( " main " , " Performs checks on the basic blocks of a function compiled ahead of time to validate its structure and coherence " ) ) ;
parser . addOption ( validateBasicBlocksOption ) ;
2018-01-22 15:46:03 +00:00
2024-05-24 11:21:44 +00:00
QCommandLineOption dumpAotStatsOption ( " dump-aot-stats " _L1 , QCoreApplication : : translate ( " main " , " Dumps statistics about ahead-of-time compilation of bindings and functions " ) ) ;
parser . addOption ( dumpAotStatsOption ) ;
QCommandLineOption moduleIdOption ( " module-id " _L1 , QCoreApplication : : translate ( " main " , " Identifies the module of the qml file being compiled for aot stats " ) , QCoreApplication : : translate ( " main " , " id " ) ) ;
parser . addOption ( moduleIdOption ) ;
2023-07-11 13:01:10 +00:00
QCommandLineOption outputFileOption ( " o " _L1 , QCoreApplication : : translate ( " main " , " Output file name " ) , QCoreApplication : : translate ( " main " , " file name " ) ) ;
2017-03-02 14:52:38 +00:00
parser . addOption ( outputFileOption ) ;
2023-07-11 13:01:10 +00:00
parser . addPositionalArgument ( " [qml file] " _L1 , " QML source file to generate cache for. " _L1 ) ;
2016-12-30 15:11:20 +00:00
2018-01-22 15:46:03 +00:00
parser . setSingleDashWordOptionMode ( QCommandLineParser : : ParseAsLongOptions ) ;
2019-07-30 14:36:53 +00:00
QStringList arguments ;
if ( ! argumentsFromCommandLineAndFile ( arguments , app . arguments ( ) ) )
return EXIT_FAILURE ;
parser . process ( arguments ) ;
2016-12-30 15:11:20 +00:00
2018-02-02 14:22:44 +00:00
enum Output {
GenerateCpp ,
GenerateCacheFile ,
2019-07-30 14:36:53 +00:00
GenerateLoader ,
GenerateLoaderStandAlone ,
2018-02-02 14:22:44 +00:00
} target = GenerateCacheFile ;
QString outputFileName ;
if ( parser . isSet ( outputFileOption ) )
outputFileName = parser . value ( outputFileOption ) ;
2023-07-11 13:01:10 +00:00
if ( outputFileName . endsWith ( " .cpp " _L1 ) ) {
2018-02-02 14:22:44 +00:00
target = GenerateCpp ;
2023-07-11 13:01:10 +00:00
if ( outputFileName . endsWith ( " qmlcache_loader.cpp " _L1 ) )
2018-02-02 14:22:44 +00:00
target = GenerateLoader ;
}
2019-07-30 14:36:53 +00:00
if ( target = = GenerateLoader & & parser . isSet ( resourceNameOption ) )
target = GenerateLoaderStandAlone ;
2024-05-24 11:21:44 +00:00
if ( parser . isSet ( dumpAotStatsOption ) & & ! parser . isSet ( moduleIdOption ) ) {
fprintf ( stderr , " --dump-aot-stats set without setting --module-id " ) ;
return EXIT_FAILURE ;
}
2017-03-03 09:35:13 +00:00
const QStringList sources = parser . positionalArguments ( ) ;
if ( sources . isEmpty ( ) ) {
parser . showHelp ( ) ;
2022-10-05 05:29:16 +00:00
} else if ( sources . size ( ) > 1 & & ( target ! = GenerateLoader & & target ! = GenerateLoaderStandAlone ) ) {
2023-07-11 13:01:10 +00:00
fprintf ( stderr , " %s \n " , qPrintable ( " Too many input files specified: ' " _L1 + sources . join ( " ' ' " _L1 ) + u ' \' ' ) ) ;
2017-03-03 09:35:13 +00:00
return EXIT_FAILURE ;
}
2019-07-30 14:36:53 +00:00
const QString inputFile = ! sources . isEmpty ( ) ? sources . first ( ) : QString ( ) ;
2018-02-02 14:22:44 +00:00
if ( outputFileName . isEmpty ( ) )
2023-07-11 13:01:10 +00:00
outputFileName = inputFile + u ' c ' ;
2017-03-02 14:52:38 +00:00
2020-10-20 11:57:38 +00:00
if ( parser . isSet ( filterResourceFileOption ) )
return qRelocateResourceFile ( inputFile , outputFileName ) ;
2018-01-22 15:46:03 +00:00
2018-02-02 14:22:44 +00:00
if ( target = = GenerateLoader ) {
2020-10-01 12:42:01 +00:00
QQmlJSResourceFileMapper mapper ( sources ) ;
2018-01-22 15:46:03 +00:00
2020-10-20 14:01:05 +00:00
QQmlJSCompileError error ;
2021-02-22 15:00:56 +00:00
if ( ! qQmlJSGenerateLoader (
mapper . resourcePaths ( QQmlJSResourceFileMapper : : allQmlJSFilter ( ) ) ,
outputFileName , parser . values ( resourceFileMappingOption ) , & error . message ) ) {
2023-07-11 13:01:10 +00:00
error . augment ( " Error generating loader stub: " _L1 ) . print ( ) ;
2018-01-22 15:46:03 +00:00
return EXIT_FAILURE ;
}
return EXIT_SUCCESS ;
}
2019-07-30 14:36:53 +00:00
if ( target = = GenerateLoaderStandAlone ) {
2020-10-20 14:01:05 +00:00
QQmlJSCompileError error ;
2020-10-20 13:07:59 +00:00
if ( ! qQmlJSGenerateLoader ( sources , outputFileName ,
parser . values ( resourceNameOption ) , & error . message ) ) {
2023-07-11 13:01:10 +00:00
error . augment ( " Error generating loader stub: " _L1 ) . print ( ) ;
2019-07-30 14:36:53 +00:00
return EXIT_FAILURE ;
}
return EXIT_SUCCESS ;
}
2018-01-22 15:46:03 +00:00
QString inputFileUrl = inputFile ;
2020-10-20 14:01:05 +00:00
QQmlJSSaveFunction saveFunction ;
2021-11-23 18:00:29 +00:00
QQmlJSResourceFileMapper fileMapper ( parser . values ( resourceOption ) ) ;
QString inputResourcePath = parser . value ( resourcePathOption ) ;
// If the user didn't specify the resource path corresponding to the file on disk being
// compiled, try to determine it from the resource file, if one was supplied.
if ( inputResourcePath . isEmpty ( ) ) {
const QStringList resourcePaths = fileMapper . resourcePaths (
QQmlJSResourceFileMapper : : localFileFilter ( inputFile ) ) ;
if ( target = = GenerateCpp & & resourcePaths . isEmpty ( ) ) {
fprintf ( stderr , " No resource path for file: %s \n " , qPrintable ( inputFile ) ) ;
return EXIT_FAILURE ;
}
2018-01-22 15:46:03 +00:00
2021-11-23 18:00:29 +00:00
if ( resourcePaths . size ( ) = = 1 ) {
2018-01-22 15:46:03 +00:00
inputResourcePath = resourcePaths . first ( ) ;
2021-11-23 18:00:29 +00:00
} else if ( target = = GenerateCpp ) {
fprintf ( stderr , " Multiple resource paths for file %s. "
" Use the --%s option to disambiguate: \n " ,
qPrintable ( inputFile ) ,
qPrintable ( resourcePathOption . names ( ) . first ( ) ) ) ;
for ( const QString & resourcePath : resourcePaths )
fprintf ( stderr , " \t %s \n " , qPrintable ( resourcePath ) ) ;
return EXIT_FAILURE ;
2018-01-22 15:46:03 +00:00
}
2021-11-23 18:00:29 +00:00
}
2018-01-22 15:46:03 +00:00
2021-11-23 18:00:29 +00:00
if ( target = = GenerateCpp ) {
2023-07-11 13:01:10 +00:00
inputFileUrl = " qrc:// " _L1 + inputResourcePath ;
2019-05-07 10:47:33 +00:00
saveFunction = [ inputResourcePath , outputFileName ] (
2019-05-13 09:07:39 +00:00
const QV4 : : CompiledData : : SaveableUnitPointer & unit ,
2020-10-20 14:01:05 +00:00
const QQmlJSAotFunctionMap & aotFunctions ,
2019-05-07 10:47:33 +00:00
QString * errorString ) {
2023-07-11 13:01:10 +00:00
return qSaveQmlJSUnitAsCpp ( inputResourcePath , outputFileName , unit , aotFunctions , errorString ) ;
2018-01-22 15:46:03 +00:00
} ;
} else {
2019-05-13 09:07:39 +00:00
saveFunction = [ outputFileName ] ( const QV4 : : CompiledData : : SaveableUnitPointer & unit ,
2020-10-20 14:01:05 +00:00
const QQmlJSAotFunctionMap & aotFunctions ,
2019-05-07 10:47:33 +00:00
QString * errorString ) {
2020-10-20 14:01:05 +00:00
Q_UNUSED ( aotFunctions ) ;
2019-06-14 09:30:47 +00:00
return unit . saveToDisk < char > (
[ & outputFileName , errorString ] ( const char * data , quint32 size ) {
return QV4 : : CompiledData : : SaveableUnitPointer : : writeDataToFile (
outputFileName , data , size , errorString ) ;
} ) ;
2018-01-22 15:46:03 +00:00
} ;
}
2023-07-11 13:01:10 +00:00
if ( inputFile . endsWith ( " .qml " _L1 ) ) {
2020-10-20 14:01:05 +00:00
QQmlJSCompileError error ;
2021-11-23 18:00:29 +00:00
if ( target ! = GenerateCpp | | inputResourcePath . isEmpty ( ) | | parser . isSet ( onlyBytecode ) ) {
if ( ! qCompileQmlFile ( inputFile , saveFunction , nullptr , & error ,
/* storeSourceLocation */ false ) ) {
2023-07-11 13:01:10 +00:00
error . augment ( " Error compiling qml file: " _L1 ) . print ( ) ;
2021-11-23 18:00:29 +00:00
return EXIT_FAILURE ;
}
} else {
QStringList importPaths ;
2023-01-23 09:03:00 +00:00
if ( parser . isSet ( resourceOption ) ) {
2023-07-11 13:01:10 +00:00
importPaths . append ( " qt-project.org/imports " _L1 ) ;
importPaths . append ( " qt/qml " _L1 ) ;
2023-01-23 09:03:00 +00:00
} ;
2021-11-23 18:00:29 +00:00
if ( parser . isSet ( importPathOption ) )
2023-01-23 09:03:00 +00:00
importPaths . append ( parser . values ( importPathOption ) ) ;
2021-11-23 18:00:29 +00:00
2022-09-08 14:18:34 +00:00
if ( ! parser . isSet ( bareOption ) )
importPaths . append ( QLibraryInfo : : path ( QLibraryInfo : : QmlImportsPath ) ) ;
2021-11-23 18:00:29 +00:00
QQmlJSImporter importer (
importPaths , parser . isSet ( resourceOption ) ? & fileMapper : nullptr ) ;
QQmlJSLogger logger ;
// Always trigger the qFatal() on "pragma Strict" violations.
2022-10-13 07:19:56 +00:00
logger . setCategoryLevel ( qmlCompiler , QtWarningMsg ) ;
2022-05-30 12:52:03 +00:00
logger . setCategoryIgnored ( qmlCompiler , false ) ;
2022-10-13 07:19:56 +00:00
logger . setCategoryFatal ( qmlCompiler , true ) ;
2021-11-23 18:00:29 +00:00
2023-12-20 12:50:19 +00:00
if ( ! parser . isSet ( verboseOption ) & & ! parser . isSet ( warningsAreErrorsOption ) )
2021-11-23 18:00:29 +00:00
logger . setSilent ( true ) ;
QQmlJSAotCompiler cppCodeGen (
2024-05-16 13:53:23 +00:00
& importer , u ' : ' + inputResourcePath ,
QQmlJSUtils : : cleanPaths ( parser . values ( importsOption ) ) , & logger ) ;
2021-11-23 18:00:29 +00:00
2024-05-24 11:21:44 +00:00
if ( parser . isSet ( dumpAotStatsOption ) ) {
QQmlJS : : QQmlJSAotCompilerStats : : setRecordAotStats ( true ) ;
QQmlJS : : QQmlJSAotCompilerStats : : setModuleId ( parser . value ( moduleIdOption ) ) ;
}
2023-07-11 13:01:10 +00:00
if ( parser . isSet ( validateBasicBlocksOption ) )
cppCodeGen . m_flags . setFlag ( QQmlJSAotCompiler : : ValidateBasicBlocks ) ;
2021-11-23 18:00:29 +00:00
if ( ! qCompileQmlFile ( inputFile , saveFunction , & cppCodeGen , & error ,
/* storeSourceLocation */ true ) ) {
2023-07-11 13:01:10 +00:00
error . augment ( " Error compiling qml file: " _L1 ) . print ( ) ;
2021-11-23 18:00:29 +00:00
return EXIT_FAILURE ;
}
2022-01-18 12:08:09 +00:00
QList < QQmlJS : : DiagnosticMessage > warnings = importer . takeGlobalWarnings ( ) ;
if ( ! warnings . isEmpty ( ) ) {
2023-07-11 13:01:10 +00:00
logger . log ( " Type warnings occurred while compiling file: " _L1 ,
2022-05-30 12:52:03 +00:00
qmlImport , QQmlJS : : SourceLocation ( ) ) ;
logger . processMessages ( warnings , qmlImport ) ;
2023-12-20 12:50:19 +00:00
if ( parser . isSet ( warningsAreErrorsOption ) )
return EXIT_FAILURE ;
2022-01-18 12:08:09 +00:00
}
2024-05-24 11:21:44 +00:00
if ( parser . isSet ( dumpAotStatsOption ) )
QQmlJS : : QQmlJSAotCompilerStats : : instance ( ) - > saveToDisk ( outputFileName + u " .aotstats " _s ) ;
2016-12-30 15:11:20 +00:00
}
2023-07-11 13:01:10 +00:00
} else if ( inputFile . endsWith ( " .js " _L1 ) | | inputFile . endsWith ( " .mjs " _L1 ) ) {
2020-10-20 14:01:05 +00:00
QQmlJSCompileError error ;
if ( ! qCompileJSFile ( inputFile , inputFileUrl , saveFunction , & error ) ) {
2023-07-11 13:01:10 +00:00
error . augment ( " Error compiling js file: " _L1 ) . print ( ) ;
2016-12-30 15:11:20 +00:00
return EXIT_FAILURE ;
}
} else {
2018-02-02 14:22:44 +00:00
fprintf ( stderr , " Ignoring %s input file as it is not QML source code - maybe remove from QML_FILES? \n " , qPrintable ( inputFile ) ) ;
2023-12-20 12:50:19 +00:00
if ( parser . isSet ( warningsAreErrorsOption ) )
return EXIT_FAILURE ;
2018-02-02 14:22:44 +00:00
}
2016-12-30 15:11:20 +00:00
return EXIT_SUCCESS ;
}