2016-12-30 15:11:20 +00:00
/****************************************************************************
* *
* * Copyright ( C ) 2016 The Qt Company Ltd .
* * Contact : https : //www.qt.io/licensing/
* *
* * This file is part of the QtQml module of the Qt Toolkit .
* *
* * $ QT_BEGIN_LICENSE : GPL - EXCEPT $
* * Commercial License Usage
* * Licensees holding valid commercial Qt licenses may use this file in
* * accordance with the commercial license agreement provided with the
* * Software or , alternatively , in accordance with the terms contained in
* * a written agreement between you and The Qt Company . For licensing terms
* * and conditions see https : //www.qt.io/terms-conditions. For further
* * information use the contact form at https : //www.qt.io/contact-us.
* *
* * GNU General Public License Usage
* * Alternatively , this file may be used under the terms of the GNU
* * General Public License version 3 as published by the Free Software
* * Foundation with exceptions as appearing in the file LICENSE . GPL3 - EXCEPT
* * included in the packaging of this file . Please review the following
* * information to ensure the GNU General Public License requirements will
* * be met : https : //www.gnu.org/licenses/gpl-3.0.html.
* *
* * $ QT_END_LICENSE $
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# 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>
# include <private/qqmljsparser_p.h>
2018-02-12 13:03:13 +00:00
# include <private/qqmljslexer_p.h>
2020-10-01 12:42:01 +00:00
# include <private/qqmljsresourcefilemapper_p.h>
2020-10-20 13:07:59 +00:00
# include <private/qqmljsloadergenerator_p.h>
2020-10-20 14:01:05 +00:00
# include <private/qqmljscompiler_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>
2021-11-23 18:00:29 +00:00
QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY ( lcAotCompiler ) ;
QT_END_NAMESPACE
2019-07-09 12:30:24 +00:00
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
if ( argument . startsWith ( QLatin1Char ( ' @ ' ) ) ) {
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.
2017-03-14 18:26:14 +00:00
qSetGlobalQHashSeed ( 0 ) ;
2016-12-30 15:11:20 +00:00
QCoreApplication app ( argc , argv ) ;
QCoreApplication : : setApplicationName ( QStringLiteral ( " qmlcachegen " ) ) ;
QCoreApplication : : setApplicationVersion ( QLatin1String ( QT_VERSION_STR ) ) ;
QCommandLineParser parser ;
parser . addHelpOption ( ) ;
parser . addVersionOption ( ) ;
2018-01-22 15:46:03 +00:00
QCommandLineOption filterResourceFileOption ( QStringLiteral ( " filter-resource-file " ) , QCoreApplication : : translate ( " main " , " Filter out QML/JS files from a resource file that can be cached ahead of time instead " ) ) ;
parser . addOption ( filterResourceFileOption ) ;
2021-06-11 20:03:57 +00:00
QCommandLineOption resourceFileMappingOption ( QStringLiteral ( " resource-file-mapping " ) , 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 ) ;
QCommandLineOption resourceOption ( QStringLiteral ( " resource " ) , QCoreApplication : : translate ( " main " , " Qt resource file that might later contain one of the compiled files " ) , QCoreApplication : : translate ( " main " , " resource-file-name " ) ) ;
parser . addOption ( resourceOption ) ;
QCommandLineOption resourcePathOption ( QStringLiteral ( " resource-path " ) , QCoreApplication : : translate ( " main " , " Qt resource file path corresponding to the file being compiled " ) , QCoreApplication : : translate ( " main " , " resource-path " ) ) ;
parser . addOption ( resourcePathOption ) ;
2019-07-30 14:36:53 +00:00
QCommandLineOption resourceNameOption ( QStringLiteral ( " resource-name " ) ,
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 " ) ) ;
parser . addOption ( resourceNameOption ) ;
2020-11-30 14:28:19 +00:00
QCommandLineOption directCallsOption ( QStringLiteral ( " direct-calls " ) , 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 ) ;
2021-11-23 18:00:29 +00:00
QCommandLineOption importsOption (
QStringLiteral ( " i " ) ,
QCoreApplication : : translate ( " main " , " Import extra qmltypes " ) ,
QCoreApplication : : translate ( " main " , " qmltypes file " ) ) ;
parser . addOption ( importsOption ) ;
QCommandLineOption importPathOption (
QStringLiteral ( " I " ) ,
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 ) ;
2021-11-23 18:00:29 +00:00
QCommandLineOption onlyBytecode (
QStringLiteral ( " only-bytecode " ) ,
QCoreApplication : : translate (
" main " , " Generate only byte code for bindings and functions, no C++ code " ) ) ;
parser . addOption ( onlyBytecode ) ;
2018-01-22 15:46:03 +00:00
2017-03-02 14:52:38 +00:00
QCommandLineOption outputFileOption ( QStringLiteral ( " o " ) , QCoreApplication : : translate ( " main " , " Output file name " ) , QCoreApplication : : translate ( " main " , " file name " ) ) ;
parser . addOption ( outputFileOption ) ;
2016-12-30 15:11:20 +00:00
parser . addPositionalArgument ( QStringLiteral ( " [qml file] " ) ,
QStringLiteral ( " QML source file to generate cache for. " ) ) ;
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 ) ;
if ( outputFileName . endsWith ( QLatin1String ( " .cpp " ) ) ) {
target = GenerateCpp ;
if ( outputFileName . endsWith ( QLatin1String ( " qmlcache_loader.cpp " ) ) )
target = GenerateLoader ;
}
2019-07-30 14:36:53 +00:00
if ( target = = GenerateLoader & & parser . isSet ( resourceNameOption ) )
target = GenerateLoaderStandAlone ;
2017-03-03 09:35:13 +00:00
const QStringList sources = parser . positionalArguments ( ) ;
if ( sources . isEmpty ( ) ) {
parser . showHelp ( ) ;
2019-07-30 14:36:53 +00:00
} else if ( sources . count ( ) > 1 & & ( target ! = GenerateLoader & & target ! = GenerateLoaderStandAlone ) ) {
2017-03-03 09:35:13 +00:00
fprintf ( stderr , " %s \n " , qPrintable ( QStringLiteral ( " Too many input files specified: ' " ) + sources . join ( QStringLiteral ( " ' ' " ) ) + QLatin1Char ( ' \' ' ) ) ) ;
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 ( ) )
outputFileName = inputFile + QLatin1Char ( ' 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 ) ) {
2018-01-22 15:46:03 +00:00
error . augment ( QLatin1String ( " Error generating loader stub: " ) ) . print ( ) ;
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 ) ) {
2019-07-30 14:36:53 +00:00
error . augment ( QLatin1String ( " Error generating loader stub: " ) ) . print ( ) ;
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 ) {
2018-01-22 15:46:03 +00:00
inputFileUrl = QStringLiteral ( " qrc:// " ) + 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 ) {
2020-10-20 14:01:05 +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
} ;
}
2016-12-30 15:11:20 +00:00
if ( inputFile . endsWith ( QLatin1String ( " .qml " ) ) ) {
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 ) ) {
error . augment ( QStringLiteral ( " Error compiling qml file: " ) ) . print ( ) ;
return EXIT_FAILURE ;
}
} else {
QStringList importPaths ;
if ( parser . isSet ( importPathOption ) )
importPaths = parser . values ( importPathOption ) ;
importPaths . append ( QLibraryInfo : : path ( QLibraryInfo : : QmlImportsPath ) ) ;
QQmlJSImporter importer (
importPaths , parser . isSet ( resourceOption ) ? & fileMapper : nullptr ) ;
QQmlJSLogger logger ;
// Always trigger the qFatal() on "pragma Strict" violations.
logger . setCategoryError ( Log_Compiler , true ) ;
// By default, we're completely silent,
// as the lcAotCompiler category default is QtFatalMsg
if ( lcAotCompiler ( ) . isDebugEnabled ( ) )
logger . setCategoryLevel ( Log_Compiler , QtDebugMsg ) ;
else if ( lcAotCompiler ( ) . isInfoEnabled ( ) )
logger . setCategoryLevel ( Log_Compiler , QtInfoMsg ) ;
else if ( lcAotCompiler ( ) . isWarningEnabled ( ) )
logger . setCategoryLevel ( Log_Compiler , QtWarningMsg ) ;
else if ( lcAotCompiler ( ) . isCriticalEnabled ( ) )
logger . setCategoryLevel ( Log_Compiler , QtCriticalMsg ) ;
else
logger . setSilent ( true ) ;
QQmlJSAotCompiler cppCodeGen (
& importer , u ' : ' + inputResourcePath , parser . values ( importsOption ) , & logger ) ;
if ( ! qCompileQmlFile ( inputFile , saveFunction , & cppCodeGen , & error ,
/* storeSourceLocation */ true ) ) {
error . augment ( QStringLiteral ( " Error compiling qml file: " ) ) . print ( ) ;
return EXIT_FAILURE ;
}
2016-12-30 15:11:20 +00:00
}
2018-08-16 11:06:22 +00:00
} else if ( inputFile . endsWith ( QLatin1String ( " .js " ) ) | | inputFile . endsWith ( QLatin1String ( " .mjs " ) ) ) {
2020-10-20 14:01:05 +00:00
QQmlJSCompileError error ;
if ( ! qCompileJSFile ( inputFile , inputFileUrl , saveFunction , & error ) ) {
2018-02-20 15:21:38 +00:00
error . augment ( QLatin1String ( " Error compiling js file: " ) ) . 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 ) ) ;
}
2016-12-30 15:11:20 +00:00
return EXIT_SUCCESS ;
}