2022-05-10 10:06:48 +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
2011-04-27 10:05:43 +00:00
# include "metamakefile.h"
# include "qdir.h"
# include "qdebug.h"
# include "makefile.h"
# include "project.h"
# include "cachekeys.h"
2019-07-31 11:53:24 +00:00
# include <algorithm>
# include <iterator>
# include <utility>
2011-04-27 10:05:43 +00:00
# define BUILDSMETATYPE 1
# define SUBDIRSMETATYPE 2
QT_BEGIN_NAMESPACE
MetaMakefileGenerator : : ~ MetaMakefileGenerator ( )
{
if ( own_project )
delete project ;
}
# ifndef QT_QMAKE_PARSER_ONLY
class BuildsMetaMakefileGenerator : public MetaMakefileGenerator
{
private :
bool init_flag ;
struct Build {
QString name , build ;
MakefileGenerator * makefile ;
} ;
QList < Build * > makefiles ;
void clearBuilds ( ) ;
2012-09-06 10:21:38 +00:00
MakefileGenerator * processBuild ( const ProString & ) ;
2019-07-30 11:16:26 +00:00
void accumulateVariableFromBuilds ( const ProKey & name , Build * build ) const ;
2019-07-31 11:53:24 +00:00
void checkForConflictingTargets ( ) const ;
2011-04-27 10:05:43 +00:00
public :
BuildsMetaMakefileGenerator ( QMakeProject * p , const QString & n , bool op ) : MetaMakefileGenerator ( p , n , op ) , init_flag ( false ) { }
2018-08-02 21:34:53 +00:00
~ BuildsMetaMakefileGenerator ( ) { clearBuilds ( ) ; }
2011-04-27 10:05:43 +00:00
2018-08-02 21:34:53 +00:00
bool init ( ) override ;
int type ( ) const override { return BUILDSMETATYPE ; }
bool write ( ) override ;
2011-04-27 10:05:43 +00:00
} ;
void
BuildsMetaMakefileGenerator : : clearBuilds ( )
{
for ( int i = 0 ; i < makefiles . count ( ) ; i + + ) {
Build * build = makefiles [ i ] ;
if ( QMakeProject * p = build - > makefile - > projectFile ( ) ) {
if ( p ! = project )
delete p ;
}
delete build - > makefile ;
delete build ;
}
makefiles . clear ( ) ;
}
bool
BuildsMetaMakefileGenerator : : init ( )
{
if ( init_flag )
return false ;
init_flag = true ;
2012-09-06 10:21:38 +00:00
const ProStringList & builds = project - > values ( " BUILDS " ) ;
2011-04-27 10:05:43 +00:00
bool use_single_build = builds . isEmpty ( ) ;
if ( builds . count ( ) > 1 & & Option : : output . fileName ( ) = = " - " ) {
use_single_build = true ;
warn_msg ( WarnLogic , " Cannot direct to stdout when using multiple BUILDS. " ) ;
}
if ( ! use_single_build ) {
for ( int i = 0 ; i < builds . count ( ) ; i + + ) {
2012-09-06 10:21:38 +00:00
ProString build = builds [ i ] ;
2011-04-27 10:05:43 +00:00
MakefileGenerator * makefile = processBuild ( build ) ;
if ( ! makefile )
return false ;
if ( ! makefile - > supportsMetaBuild ( ) ) {
warn_msg ( WarnLogic , " QMAKESPEC does not support multiple BUILDS. " ) ;
clearBuilds ( ) ;
use_single_build = true ;
break ;
} else {
Build * b = new Build ;
b - > name = name ;
if ( builds . count ( ) ! = 1 )
2012-09-06 10:21:38 +00:00
b - > build = build . toQString ( ) ;
2011-04-27 10:05:43 +00:00
b - > makefile = makefile ;
makefiles + = b ;
}
}
}
if ( use_single_build ) {
Build * build = new Build ;
build - > name = name ;
build - > makefile = createMakefileGenerator ( project , false ) ;
if ( build - > makefile ) {
makefiles + = build ;
} else {
delete build ;
return false ;
}
}
return true ;
}
bool
2013-02-25 18:54:48 +00:00
BuildsMetaMakefileGenerator : : write ( )
2011-04-27 10:05:43 +00:00
{
2018-08-02 22:22:24 +00:00
Build * glue = nullptr ;
2019-10-02 14:53:45 +00:00
if ( ! makefiles . isEmpty ( ) & & ! makefiles . first ( ) - > build . isNull ( )
& & Option : : qmake_mode ! = Option : : QMAKE_GENERATE_PRL ) {
2011-04-27 10:05:43 +00:00
glue = new Build ;
glue - > name = name ;
glue - > makefile = createMakefileGenerator ( project , true ) ;
makefiles + = glue ;
}
bool ret = true ;
const QString & output_name = Option : : output . fileName ( ) ;
for ( int i = 0 ; ret & & i < makefiles . count ( ) ; i + + ) {
Option : : output . setFileName ( output_name ) ;
Build * build = makefiles [ i ] ;
bool using_stdout = false ;
if ( build - > makefile & & ( Option : : qmake_mode = = Option : : QMAKE_GENERATE_MAKEFILE | |
Option : : qmake_mode = = Option : : QMAKE_GENERATE_PROJECT )
& & ( ! build - > makefile - > supportsMergedBuilds ( )
| | ( build - > makefile - > supportsMergedBuilds ( ) & & ( ! glue | | build = = glue ) ) ) ) {
//open output
if ( ! ( Option : : output . isOpen ( ) ) ) {
if ( Option : : output . fileName ( ) = = " - " ) {
Option : : output . setFileName ( " " ) ;
Option : : output_dir = qmake_getpwd ( ) ;
Option : : output . open ( stdout , QIODevice : : WriteOnly | QIODevice : : Text ) ;
using_stdout = true ;
} else {
if ( Option : : output . fileName ( ) . isEmpty ( ) & &
Option : : qmake_mode = = Option : : QMAKE_GENERATE_MAKEFILE )
2012-09-06 10:21:38 +00:00
Option : : output . setFileName ( project - > first ( " QMAKE_MAKEFILE " ) . toQString ( ) ) ;
2011-04-27 10:05:43 +00:00
QString build_name = build - > name ;
if ( ! build - > build . isEmpty ( ) ) {
if ( ! build_name . isEmpty ( ) )
build_name + = " . " ;
build_name + = build - > build ;
}
if ( ! build - > makefile - > openOutput ( Option : : output , build_name ) ) {
fprintf ( stderr , " Failure to open file: %s \n " ,
Option : : output . fileName ( ) . isEmpty ( ) ? " (stdout) " :
Option : : output . fileName ( ) . toLatin1 ( ) . constData ( ) ) ;
return false ;
}
}
}
} else {
using_stdout = true ; //kind of..
}
if ( ! build - > makefile ) {
ret = false ;
} else if ( build = = glue ) {
2019-07-31 11:53:24 +00:00
checkForConflictingTargets ( ) ;
2019-07-30 11:16:26 +00:00
accumulateVariableFromBuilds ( " QMAKE_INTERNAL_INCLUDED_FILES " , build ) ;
2011-04-27 10:05:43 +00:00
ret = build - > makefile - > writeProjectMakefile ( ) ;
} else {
ret = build - > makefile - > write ( ) ;
if ( glue & & glue - > makefile - > supportsMergedBuilds ( ) )
ret = glue - > makefile - > mergeBuildProject ( build - > makefile ) ;
}
if ( ! using_stdout ) {
Option : : output . close ( ) ;
if ( ! ret )
Option : : output . remove ( ) ;
}
}
return ret ;
}
MakefileGenerator
2012-09-06 10:21:38 +00:00
* BuildsMetaMakefileGenerator : : processBuild ( const ProString & build )
2011-04-27 10:05:43 +00:00
{
if ( project ) {
debug_msg ( 1 , " Meta Generator: Parsing '%s' for build [%s]. " ,
project - > projectFile ( ) . toLatin1 ( ) . constData ( ) , build . toLatin1 ( ) . constData ( ) ) ;
//initialize the base
2012-09-06 10:21:38 +00:00
ProValueMap basevars ;
ProStringList basecfgs = project - > values ( ProKey ( build + " .CONFIG " ) ) ;
2012-02-21 11:44:27 +00:00
basecfgs + = build ;
basecfgs + = " build_pass " ;
2012-09-06 10:21:38 +00:00
basevars [ " BUILD_PASS " ] = ProStringList ( build ) ;
ProStringList buildname = project - > values ( ProKey ( build + " .name " ) ) ;
basevars [ " BUILD_NAME " ] = ( buildname . isEmpty ( ) ? ProStringList ( build ) : buildname ) ;
2011-04-27 10:05:43 +00:00
//create project
2012-09-05 16:29:19 +00:00
QMakeProject * build_proj = new QMakeProject ;
2012-02-21 11:56:47 +00:00
build_proj - > setExtraVars ( basevars ) ;
2012-02-21 11:44:27 +00:00
build_proj - > setExtraConfigs ( basecfgs ) ;
2011-04-27 10:05:43 +00:00
2016-04-15 14:13:34 +00:00
if ( build_proj - > read ( project - > projectFile ( ) ) )
return createMakefileGenerator ( build_proj ) ;
2011-04-27 10:05:43 +00:00
}
2018-08-02 22:22:24 +00:00
return nullptr ;
2011-04-27 10:05:43 +00:00
}
2019-07-30 11:16:26 +00:00
void BuildsMetaMakefileGenerator : : accumulateVariableFromBuilds ( const ProKey & name , Build * dst ) const
{
ProStringList & values = dst - > makefile - > projectFile ( ) - > values ( name ) ;
for ( auto build : makefiles ) {
if ( build ! = dst )
values + = build - > makefile - > projectFile ( ) - > values ( name ) ;
}
values . removeDuplicates ( ) ;
}
2019-07-31 11:53:24 +00:00
void BuildsMetaMakefileGenerator : : checkForConflictingTargets ( ) const
{
if ( makefiles . count ( ) < 3 ) {
// Checking for conflicts only makes sense if we have more than one BUILD,
// and the last entry in makefiles is the "glue" Build.
return ;
}
2019-09-30 12:23:55 +00:00
if ( ! project - > isActiveConfig ( " build_all " ) ) {
// Only complain if we're about to build all configurations.
return ;
}
2019-07-31 11:53:24 +00:00
using TargetInfo = std : : pair < Build * , ProString > ;
2020-07-06 09:11:44 +00:00
QList < TargetInfo > targets ;
2019-07-31 11:53:24 +00:00
const int last = makefiles . count ( ) - 1 ;
targets . resize ( last ) ;
for ( int i = 0 ; i < last ; + + i ) {
Build * b = makefiles . at ( i ) ;
auto mkf = b - > makefile ;
auto prj = mkf - > projectFile ( ) ;
targets [ i ] = std : : make_pair ( b , prj - > first ( mkf - > fullTargetVariable ( ) ) ) ;
}
std : : stable_sort ( targets . begin ( ) , targets . end ( ) ,
[ ] ( const TargetInfo & lhs , const TargetInfo & rhs )
{
return lhs . second < rhs . second ;
} ) ;
for ( auto prev = targets . begin ( ) , it = std : : next ( prev ) ; it ! = targets . end ( ) ; + + prev , + + it ) {
if ( prev - > second = = it - > second ) {
warn_msg ( WarnLogic , " Targets of builds '%s' and '%s' conflict: %s. " ,
qPrintable ( prev - > first - > build ) ,
qPrintable ( it - > first - > build ) ,
qPrintable ( prev - > second . toQString ( ) ) ) ;
break ;
}
}
}
2011-04-27 10:05:43 +00:00
class SubdirsMetaMakefileGenerator : public MetaMakefileGenerator
{
protected :
bool init_flag ;
struct Subdir {
2018-08-02 22:22:24 +00:00
Subdir ( ) : makefile ( nullptr ) , indent ( 0 ) { }
2011-04-27 10:05:43 +00:00
~ Subdir ( ) { delete makefile ; }
QString input_dir ;
QString output_dir , output_file ;
MetaMakefileGenerator * makefile ;
int indent ;
} ;
QList < Subdir * > subs ;
MakefileGenerator * processBuild ( const QString & ) ;
public :
SubdirsMetaMakefileGenerator ( QMakeProject * p , const QString & n , bool op ) : MetaMakefileGenerator ( p , n , op ) , init_flag ( false ) { }
2018-08-02 21:34:53 +00:00
~ SubdirsMetaMakefileGenerator ( ) ;
2011-04-27 10:05:43 +00:00
2018-08-02 21:34:53 +00:00
bool init ( ) override ;
int type ( ) const override { return SUBDIRSMETATYPE ; }
bool write ( ) override ;
2011-04-27 10:05:43 +00:00
} ;
bool
SubdirsMetaMakefileGenerator : : init ( )
{
if ( init_flag )
return false ;
init_flag = true ;
bool hasError = false ;
2012-05-04 18:02:35 +00:00
bool recurse = Option : : recursive ;
2012-05-10 14:49:13 +00:00
if ( recurse & & project - > isActiveConfig ( " dont_recurse " ) )
recurse = false ;
2011-04-27 10:05:43 +00:00
if ( recurse ) {
QString old_output_dir = Option : : output_dir ;
QString old_output = Option : : output . fileName ( ) ;
QString oldpwd = qmake_getpwd ( ) ;
QString thispwd = oldpwd ;
if ( ! thispwd . endsWith ( ' / ' ) )
thispwd + = ' / ' ;
2012-09-06 10:21:38 +00:00
const ProStringList & subdirs = project - > values ( " SUBDIRS " ) ;
2022-03-17 11:07:44 +00:00
Q_CONSTINIT static int recurseDepth = - 1 ;
2011-04-27 10:05:43 +00:00
+ + recurseDepth ;
for ( int i = 0 ; i < subdirs . size ( ) ; + + i ) {
Subdir * sub = new Subdir ;
sub - > indent = recurseDepth ;
2012-09-06 10:21:38 +00:00
QFileInfo subdir ( subdirs . at ( i ) . toQString ( ) ) ;
const ProKey fkey ( subdirs . at ( i ) + " .file " ) ;
if ( ! project - > isEmpty ( fkey ) ) {
2020-11-12 15:11:50 +00:00
subdir = QFileInfo ( project - > first ( fkey ) . toQString ( ) ) ;
2012-09-06 10:21:38 +00:00
} else {
const ProKey skey ( subdirs . at ( i ) + " .subdir " ) ;
if ( ! project - > isEmpty ( skey ) )
2020-11-12 15:11:50 +00:00
subdir = QFileInfo ( project - > first ( skey ) . toQString ( ) ) ;
2012-09-06 10:21:38 +00:00
}
2011-04-27 10:05:43 +00:00
QString sub_name ;
if ( subdir . isDir ( ) )
subdir = QFileInfo ( subdir . filePath ( ) + " / " + subdir . fileName ( ) + Option : : pro_ext ) ;
else
sub_name = subdir . baseName ( ) ;
if ( ! subdir . isRelative ( ) ) { //we can try to make it relative
QString subdir_path = subdir . filePath ( ) ;
if ( subdir_path . startsWith ( thispwd ) )
subdir = QFileInfo ( subdir_path . mid ( thispwd . length ( ) ) ) ;
}
//handle sub project
2012-09-05 16:29:19 +00:00
QMakeProject * sub_proj = new QMakeProject ;
2011-04-27 10:05:43 +00:00
for ( int ind = 0 ; ind < sub - > indent ; + + ind )
printf ( " " ) ;
sub - > input_dir = subdir . absolutePath ( ) ;
if ( subdir . isRelative ( ) & & old_output_dir ! = oldpwd ) {
2012-05-14 13:13:12 +00:00
sub - > output_dir = old_output_dir + ( subdir . path ( ) ! = " . " ? " / " + subdir . path ( ) : QString ( ) ) ;
2011-04-27 10:05:43 +00:00
printf ( " Reading %s [%s] \n " , subdir . absoluteFilePath ( ) . toLatin1 ( ) . constData ( ) , sub - > output_dir . toLatin1 ( ) . constData ( ) ) ;
} else { //what about shadow builds?
sub - > output_dir = sub - > input_dir ;
printf ( " Reading %s \n " , subdir . absoluteFilePath ( ) . toLatin1 ( ) . constData ( ) ) ;
}
qmake_setpwd ( sub - > input_dir ) ;
Option : : output_dir = sub - > output_dir ;
bool tmpError = ! sub_proj - > read ( subdir . fileName ( ) ) ;
2012-04-23 16:08:16 +00:00
if ( ! sub_proj - > isEmpty ( " QMAKE_FAILED_REQUIREMENTS " ) ) {
2011-04-27 10:05:43 +00:00
fprintf ( stderr , " Project file(%s) not recursed because all requirements not met: \n \t %s \n " ,
subdir . fileName ( ) . toLatin1 ( ) . constData ( ) ,
2012-05-18 18:00:23 +00:00
sub_proj - > values ( " QMAKE_FAILED_REQUIREMENTS " ) . join ( ' ' ) . toLatin1 ( ) . constData ( ) ) ;
2011-04-27 10:05:43 +00:00
delete sub ;
delete sub_proj ;
Option : : output_dir = old_output_dir ;
qmake_setpwd ( oldpwd ) ;
continue ;
} else {
hasError | = tmpError ;
}
sub - > makefile = MetaMakefileGenerator : : createMetaGenerator ( sub_proj , sub_name ) ;
2019-07-30 12:19:10 +00:00
const QString output_name = Option : : output . fileName ( ) ;
Option : : output . setFileName ( sub - > output_file ) ;
hasError | = ! sub - > makefile - > write ( ) ;
delete sub ;
qmakeClearCaches ( ) ;
sub = nullptr ;
Option : : output . setFileName ( output_name ) ;
2011-04-27 10:05:43 +00:00
Option : : output_dir = old_output_dir ;
qmake_setpwd ( oldpwd ) ;
}
- - recurseDepth ;
Option : : output . setFileName ( old_output ) ;
Option : : output_dir = old_output_dir ;
qmake_setpwd ( oldpwd ) ;
}
Subdir * self = new Subdir ;
self - > input_dir = qmake_getpwd ( ) ;
self - > output_dir = Option : : output_dir ;
if ( ! recurse | | ( ! Option : : output . fileName ( ) . endsWith ( Option : : dir_sep ) & & ! QFileInfo ( Option : : output ) . isDir ( ) ) )
self - > output_file = Option : : output . fileName ( ) ;
self - > makefile = new BuildsMetaMakefileGenerator ( project , name , false ) ;
self - > makefile - > init ( ) ;
subs . append ( self ) ;
return ! hasError ;
}
bool
2013-02-25 18:54:48 +00:00
SubdirsMetaMakefileGenerator : : write ( )
2011-04-27 10:05:43 +00:00
{
bool ret = true ;
const QString & pwd = qmake_getpwd ( ) ;
const QString & output_dir = Option : : output_dir ;
const QString & output_name = Option : : output . fileName ( ) ;
for ( int i = 0 ; ret & & i < subs . count ( ) ; i + + ) {
const Subdir * sub = subs . at ( i ) ;
2013-02-25 14:13:51 +00:00
qmake_setpwd ( sub - > input_dir ) ;
Option : : output_dir = QFileInfo ( sub - > output_dir ) . absoluteFilePath ( ) ;
Option : : output . setFileName ( sub - > output_file ) ;
2011-04-27 10:05:43 +00:00
if ( i ! = subs . count ( ) - 1 ) {
for ( int ind = 0 ; ind < sub - > indent ; + + ind )
printf ( " " ) ;
printf ( " Writing %s \n " , QDir : : cleanPath ( Option : : output_dir + " / " +
Option : : output . fileName ( ) ) . toLatin1 ( ) . constData ( ) ) ;
}
2013-02-25 14:13:51 +00:00
if ( ! ( ret = sub - > makefile - > write ( ) ) )
2011-04-27 10:05:43 +00:00
break ;
//restore because I'm paranoid
qmake_setpwd ( pwd ) ;
Option : : output . setFileName ( output_name ) ;
Option : : output_dir = output_dir ;
}
return ret ;
}
SubdirsMetaMakefileGenerator : : ~ SubdirsMetaMakefileGenerator ( )
{
for ( int i = 0 ; i < subs . count ( ) ; i + + )
delete subs [ i ] ;
subs . clear ( ) ;
}
//Factory things
QT_BEGIN_INCLUDE_NAMESPACE
# include "unixmake.h"
# include "mingw_make.h"
# include "projectgenerator.h"
# include "pbuilder_pbx.h"
# include "msvc_nmake.h"
# include "msvc_vcproj.h"
# include "msvc_vcxproj.h"
QT_END_INCLUDE_NAMESPACE
MakefileGenerator *
MetaMakefileGenerator : : createMakefileGenerator ( QMakeProject * proj , bool noIO )
{
2012-03-07 12:31:04 +00:00
Option : : postProcessProject ( proj ) ;
2018-08-02 22:22:24 +00:00
MakefileGenerator * mkfile = nullptr ;
2011-04-27 10:05:43 +00:00
if ( Option : : qmake_mode = = Option : : QMAKE_GENERATE_PROJECT ) {
mkfile = new ProjectGenerator ;
mkfile - > setProjectFile ( proj ) ;
return mkfile ;
}
2012-09-06 10:21:38 +00:00
ProString gen = proj - > first ( " MAKEFILE_GENERATOR " ) ;
2011-04-27 10:05:43 +00:00
if ( gen . isEmpty ( ) ) {
fprintf ( stderr , " MAKEFILE_GENERATOR variable not set as a result of parsing : %s. Possibly qmake was not able to find files included using \" include(..) \" - enable qmake debugging to investigate more. \n " ,
proj - > projectFile ( ) . toLatin1 ( ) . constData ( ) ) ;
} else if ( gen = = " UNIX " ) {
mkfile = new UnixMakefileGenerator ;
} else if ( gen = = " MINGW " ) {
mkfile = new MingwMakefileGenerator ;
} else if ( gen = = " PROJECTBUILDER " | | gen = = " XCODE " ) {
2014-07-24 14:17:34 +00:00
# ifdef Q_CC_MSVC
fprintf ( stderr , " Generating Xcode projects is not supported with an MSVC build of Qt. \n " ) ;
# else
2011-04-27 10:05:43 +00:00
mkfile = new ProjectBuilderMakefileGenerator ;
2014-07-24 14:17:34 +00:00
# endif
2011-04-27 10:05:43 +00:00
} else if ( gen = = " MSVC.NET " ) {
if ( proj - > first ( " TEMPLATE " ) . startsWith ( " vc " ) )
mkfile = new VcprojGenerator ;
else
mkfile = new NmakeMakefileGenerator ;
} else if ( gen = = " MSBUILD " ) {
// Visual Studio >= v11.0
if ( proj - > first ( " TEMPLATE " ) . startsWith ( " vc " ) )
mkfile = new VcxprojGenerator ;
else
mkfile = new NmakeMakefileGenerator ;
} else {
fprintf ( stderr , " Unknown generator specified: %s \n " , gen . toLatin1 ( ) . constData ( ) ) ;
}
if ( mkfile ) {
mkfile - > setNoIO ( noIO ) ;
mkfile - > setProjectFile ( proj ) ;
}
return mkfile ;
}
MetaMakefileGenerator *
MetaMakefileGenerator : : createMetaGenerator ( QMakeProject * proj , const QString & name , bool op , bool * success )
{
2012-03-07 12:31:04 +00:00
Option : : postProcessProject ( proj ) ;
2018-08-02 22:22:24 +00:00
MetaMakefileGenerator * ret = nullptr ;
2011-04-27 10:05:43 +00:00
if ( ( Option : : qmake_mode = = Option : : QMAKE_GENERATE_MAKEFILE | |
Option : : qmake_mode = = Option : : QMAKE_GENERATE_PRL ) ) {
if ( proj - > first ( " TEMPLATE " ) . endsWith ( " subdirs " ) )
ret = new SubdirsMetaMakefileGenerator ( proj , name , op ) ;
}
if ( ! ret )
ret = new BuildsMetaMakefileGenerator ( proj , name , op ) ;
bool res = ret - > init ( ) ;
if ( success )
* success = res ;
return ret ;
}
# endif // QT_QMAKE_PARSER_ONLY
QT_END_NAMESPACE