Files
reactos/reactos/tools/rbuild/backend/mingw/mingw.cpp
Casper Hornstrup f81cbc31f1 Accept ROS_INSTALL environment variable
svn path=/branches/xmlbuildsystem/; revision=14520
2005-04-05 20:24:26 +00:00

774 lines
18 KiB
C++

#include "../../pch.h"
#include "mingw.h"
#include <assert.h>
#include <dirent.h>
#include "modulehandler.h"
#ifdef WIN32
#define MKDIR(s) mkdir(s)
#else
#define MKDIR(s) mkdir(s, 0755)
#endif
using std::string;
using std::vector;
using std::set;
using std::map;
typedef set<string> set_string;
string
v2s ( const string_list& v, int wrap_at )
{
if ( !v.size() )
return "";
string s;
int wrap_count = 0;
for ( size_t i = 0; i < v.size(); i++ )
{
if ( !v[i].size() )
continue;
if ( wrap_at > 0 && wrap_count++ == wrap_at )
s += " \\\n\t\t";
else if ( s.size() )
s += " ";
s += v[i];
}
return s;
}
Directory::Directory ( const string& name_ )
: name(name_)
{
}
void
Directory::Add ( const char* subdir )
{
size_t i;
string s1 = string ( subdir );
if ( ( i = s1.find ( '$' ) ) != string::npos )
{
throw InvalidOperationException ( __FILE__,
__LINE__,
"No environment variables can be used here. Path was %s",
subdir );
}
const char* p = strpbrk ( subdir, "/\\" );
if ( !p )
p = subdir + strlen(subdir);
string s ( subdir, p-subdir );
if ( subdirs.find(s) == subdirs.end() )
subdirs[s] = new Directory(s);
if ( *p && *++p )
subdirs[s]->Add ( p );
}
bool
Directory::mkdir_p ( const char* path )
{
DIR *directory;
directory = opendir ( path );
if ( directory != NULL )
{
closedir ( directory );
return false;
}
if ( MKDIR ( path ) != 0 )
throw AccessDeniedException ( string ( path ) );
return true;
}
bool
Directory::CreateDirectory ( string path )
{
size_t index = 0;
size_t nextIndex;
if ( isalpha ( path[0] ) && path[1] == ':' && path[2] == CSEP )
{
nextIndex = path.find ( CSEP, 3);
}
else
nextIndex = path.find ( CSEP );
bool directoryWasCreated = false;
while ( nextIndex != string::npos )
{
nextIndex = path.find ( CSEP, index + 1 );
directoryWasCreated = mkdir_p ( path.substr ( 0, nextIndex ).c_str () );
index = nextIndex;
}
return directoryWasCreated;
}
string
Directory::ReplaceVariable ( string name,
string value,
string path )
{
size_t i = path.find ( name );
if ( i != string::npos )
return path.replace ( i, name.length (), value );
else
return path;
}
string
Directory::GetEnvironmentVariable ( const string& name )
{
char* value = getenv ( name.c_str () );
if ( value != NULL && strlen ( value ) > 0 )
return ssprintf ( "%s",
value );
else
return "";
}
string
Directory::GetEnvironmentVariablePathOrDefault ( const string& name,
const string& defaultValue )
{
const string& environmentVariableValue = GetEnvironmentVariable ( name );
if ( environmentVariableValue.length () > 0 )
return NormalizeFilename ( environmentVariableValue );
else
return defaultValue;
}
string
Directory::GetIntermediatePath ()
{
return "obj-i386";
}
string
Directory::GetOutputPath ()
{
return "output-i386";
}
string
Directory::GetInstallPath ()
{
return GetEnvironmentVariablePathOrDefault ( "ROS_INSTALL",
"reactos" );
}
void
Directory::ResolveVariablesInPath ( char* buf,
string path )
{
string s = ReplaceVariable ( "$(INTERMEDIATE)", GetIntermediatePath (), path );
s = ReplaceVariable ( "$(OUTPUT)", GetOutputPath (), s );
s = ReplaceVariable ( "$(INSTALL)", GetInstallPath (), s );
strcpy ( buf, s.c_str () );
}
void
Directory::GenerateTree ( const string& parent,
bool verbose )
{
string path;
if ( parent.size() )
{
char buf[256];
path = parent + SSEP + name;
ResolveVariablesInPath ( buf, path );
if ( CreateDirectory ( buf ) && verbose )
printf ( "Created %s\n", buf );
}
else
path = name;
for ( directory_map::iterator i = subdirs.begin();
i != subdirs.end();
++i )
{
i->second->GenerateTree ( path, verbose );
}
}
static class MingwFactory : public Backend::Factory
{
public:
MingwFactory() : Factory ( "mingw" ) {}
Backend* operator() ( Project& project, bool verbose )
{
return new MingwBackend ( project, verbose );
}
} factory;
MingwBackend::MingwBackend ( Project& project, bool verbose )
: Backend ( project, verbose ),
intermediateDirectory ( new Directory ("$(INTERMEDIATE)" ) ),
outputDirectory ( new Directory ( "$(OUTPUT)" ) ),
installDirectory ( new Directory ( "$(INSTALL)" ) )
{
}
MingwBackend::~MingwBackend()
{
delete intermediateDirectory;
delete outputDirectory;
delete installDirectory;
}
string
MingwBackend::AddDirectoryTarget ( const string& directory,
Directory* directoryTree )
{
directoryTree->Add ( directory.c_str() );
return directoryTree->name;
}
void
MingwBackend::ProcessModules ()
{
printf ( "Processing modules..." );
vector<MingwModuleHandler*> v;
size_t i;
for ( i = 0; i < ProjectNode.modules.size (); i++ )
{
Module& module = *ProjectNode.modules[i];
MingwModuleHandler* h = MingwModuleHandler::InstanciateHandler (
module,
this );
if ( module.host == HostDefault )
{
module.host = h->DefaultHost();
assert ( module.host != HostDefault );
}
v.push_back ( h );
}
size_t iend = v.size ();
for ( i = 0; i < iend; i++ )
v[i]->GenerateObjectMacro();
fprintf ( fMakefile, "\n" );
for ( i = 0; i < iend; i++ )
v[i]->GenerateTargetMacro();
fprintf ( fMakefile, "\n" );
GenerateAllTarget ( v );
GenerateInitTarget ();
for ( i = 0; i < iend; i++ )
v[i]->GenerateOtherMacros();
for ( i = 0; i < iend; i++ )
{
MingwModuleHandler& h = *v[i];
h.GeneratePreconditionDependencies ();
h.Process ();
h.GenerateInvocations ();
h.GenerateCleanTarget ();
delete v[i];
}
printf ( "done\n" );
}
void
MingwBackend::Process ()
{
DetectPipeSupport ();
DetectPCHSupport ();
CreateMakefile ();
GenerateHeader ();
GenerateGlobalVariables ();
GenerateXmlBuildFilesMacro ();
ProcessModules ();
GenerateInstallTarget ();
GenerateDirectories ();
CheckAutomaticDependencies ();
CloseMakefile ();
}
void
MingwBackend::CreateMakefile ()
{
fMakefile = fopen ( ProjectNode.makefile.c_str (), "w" );
if ( !fMakefile )
throw AccessDeniedException ( ProjectNode.makefile );
MingwModuleHandler::SetBackend ( this );
MingwModuleHandler::SetMakefile ( fMakefile );
MingwModuleHandler::SetUsePch ( use_pch );
}
void
MingwBackend::CloseMakefile () const
{
if (fMakefile)
fclose ( fMakefile );
}
void
MingwBackend::GenerateHeader () const
{
fprintf ( fMakefile, "# THIS FILE IS AUTOMATICALLY GENERATED, EDIT 'ReactOS.xml' INSTEAD\n\n" );
}
void
MingwBackend::GenerateProjectCFlagsMacro ( const char* assignmentOperation,
IfableData& data ) const
{
size_t i;
fprintf (
fMakefile,
"PROJECT_CFLAGS %s",
assignmentOperation );
for ( i = 0; i < data.includes.size(); i++ )
{
fprintf (
fMakefile,
" -I%s",
data.includes[i]->directory.c_str() );
}
for ( i = 0; i < data.defines.size(); i++ )
{
Define& d = *data.defines[i];
fprintf (
fMakefile,
" -D%s",
d.name.c_str() );
if ( d.value.size() )
fprintf (
fMakefile,
"=%s",
d.value.c_str() );
}
fprintf ( fMakefile, "\n" );
}
void
MingwBackend::GenerateGlobalCFlagsAndProperties (
const char* assignmentOperation,
IfableData& data ) const
{
size_t i;
for ( i = 0; i < data.properties.size(); i++ )
{
Property& prop = *data.properties[i];
fprintf ( fMakefile, "%s := %s\n",
prop.name.c_str(),
prop.value.c_str() );
}
if ( data.includes.size() || data.defines.size() )
{
GenerateProjectCFlagsMacro ( assignmentOperation,
data );
}
for ( i = 0; i < data.ifs.size(); i++ )
{
If& rIf = *data.ifs[i];
if ( rIf.data.defines.size()
|| rIf.data.includes.size()
|| rIf.data.ifs.size() )
{
fprintf (
fMakefile,
"ifeq (\"$(%s)\",\"%s\")\n",
rIf.property.c_str(),
rIf.value.c_str() );
GenerateGlobalCFlagsAndProperties (
"+=",
rIf.data );
fprintf (
fMakefile,
"endif\n\n" );
}
}
}
string
MingwBackend::GenerateProjectLFLAGS () const
{
string lflags;
for ( size_t i = 0; i < ProjectNode.linkerFlags.size (); i++ )
{
LinkerFlag& linkerFlag = *ProjectNode.linkerFlags[i];
if ( lflags.length () > 0 )
lflags += " ";
lflags += linkerFlag.flag;
}
return lflags;
}
void
MingwBackend::GenerateGlobalVariables () const
{
GenerateGlobalCFlagsAndProperties (
"=",
ProjectNode.non_if_data );
fprintf ( fMakefile, "PROJECT_RCFLAGS = $(PROJECT_CFLAGS)\n" );
fprintf ( fMakefile, "PROJECT_LFLAGS = %s\n",
GenerateProjectLFLAGS ().c_str () );
fprintf ( fMakefile, "\n" );
}
bool
MingwBackend::IncludeInAllTarget ( const Module& module ) const
{
if ( module.type == ObjectLibrary )
return false;
if ( module.type == BootSector )
return false;
if ( module.type == Iso )
return false;
return true;
}
void
MingwBackend::GenerateAllTarget ( const vector<MingwModuleHandler*>& handlers ) const
{
fprintf ( fMakefile, "all:" );
int wrap_count = 0;
size_t iend = handlers.size ();
for ( size_t i = 0; i < iend; i++ )
{
const Module& module = handlers[i]->module;
if ( IncludeInAllTarget ( module ) )
{
if ( wrap_count++ == 5 )
fprintf ( fMakefile, " \\\n\t\t" ), wrap_count = 0;
fprintf ( fMakefile,
" %s",
GetTargetMacro(module).c_str () );
}
}
fprintf ( fMakefile, "\n\t\n\n" );
}
string
MingwBackend::GetBuildToolDependencies () const
{
string dependencies;
for ( size_t i = 0; i < ProjectNode.modules.size (); i++ )
{
Module& module = *ProjectNode.modules[i];
if ( module.type == BuildTool )
{
if ( dependencies.length () > 0 )
dependencies += " ";
dependencies += module.GetDependencyPath ();
}
}
return dependencies;
}
void
MingwBackend::GenerateInitTarget () const
{
fprintf ( fMakefile,
"INIT = %s\n",
GetBuildToolDependencies ().c_str () );
fprintf ( fMakefile, "\n" );
}
void
MingwBackend::GenerateXmlBuildFilesMacro() const
{
fprintf ( fMakefile,
"XMLBUILDFILES = %s \\\n",
ProjectNode.GetProjectFilename ().c_str () );
string xmlbuildFilenames;
int numberOfExistingFiles = 0;
for ( size_t i = 0; i < ProjectNode.xmlbuildfiles.size (); i++ )
{
XMLInclude& xmlbuildfile = *ProjectNode.xmlbuildfiles[i];
if ( !xmlbuildfile.fileExists )
continue;
numberOfExistingFiles++;
if ( xmlbuildFilenames.length () > 0 )
xmlbuildFilenames += " ";
xmlbuildFilenames += NormalizeFilename ( xmlbuildfile.topIncludeFilename );
if ( numberOfExistingFiles % 5 == 4 || i == ProjectNode.xmlbuildfiles.size () - 1 )
{
fprintf ( fMakefile,
"\t%s",
xmlbuildFilenames.c_str ());
if ( i == ProjectNode.xmlbuildfiles.size () - 1 )
{
fprintf ( fMakefile, "\n" );
}
else
{
fprintf ( fMakefile,
" \\\n",
xmlbuildFilenames.c_str () );
}
xmlbuildFilenames.resize ( 0 );
}
numberOfExistingFiles++;
}
fprintf ( fMakefile, "\n" );
}
void
MingwBackend::CheckAutomaticDependencies ()
{
printf ( "Checking automatic dependencies..." );
AutomaticDependency automaticDependency ( ProjectNode );
automaticDependency.Process ();
automaticDependency.CheckAutomaticDependencies ( verbose );
printf ( "done\n" );
}
bool
MingwBackend::IncludeDirectoryTarget ( const string& directory ) const
{
if ( directory == "$(INTERMEDIATE)" SSEP "tools")
return false;
else
return true;
}
void
MingwBackend::GenerateDirectories ()
{
printf ( "Creating directories..." );
intermediateDirectory->GenerateTree ( "", verbose );
outputDirectory->GenerateTree ( "", verbose );
installDirectory->GenerateTree ( "", verbose );
printf ( "done\n" );
}
void
MingwBackend::DetectPipeSupport ()
{
printf ( "Detecting compiler -pipe support..." );
string pipe_detection = "tools" SSEP "rbuild" SSEP "backend" SSEP "mingw" SSEP "pipe_detection.c";
string pipe_detectionObjectFilename = ReplaceExtension ( pipe_detection,
".o" );
string command = ssprintf (
"gcc -pipe -c %s -o %s 2>%s",
pipe_detection.c_str (),
pipe_detectionObjectFilename.c_str (),
NUL );
int exitcode = system ( command.c_str () );
FILE* f = fopen ( pipe_detectionObjectFilename.c_str (), "rb" );
if ( f )
{
usePipe = (exitcode == 0);
fclose ( f );
unlink ( pipe_detectionObjectFilename.c_str () );
}
else
usePipe = false;
if ( usePipe )
printf ( "detected\n" );
else
printf ( "not detected\n" );
}
void
MingwBackend::DetectPCHSupport ()
{
printf ( "Detecting compiler pre-compiled header support..." );
string path = "tools" SSEP "rbuild" SSEP "backend" SSEP "mingw" SSEP "pch_detection.h";
string cmd = ssprintf (
"gcc -c %s 2>%s",
path.c_str (),
NUL );
system ( cmd.c_str () );
path += ".gch";
FILE* f = fopen ( path.c_str (), "rb" );
if ( f )
{
use_pch = true;
fclose ( f );
unlink ( path.c_str () );
}
else
use_pch = false;
if ( use_pch )
printf ( "detected\n" );
else
printf ( "not detected\n" );
}
void
MingwBackend::GetNonModuleInstallTargetFiles (
vector<string>& out ) const
{
for ( size_t i = 0; i < ProjectNode.installfiles.size (); i++ )
{
const InstallFile& installfile = *ProjectNode.installfiles[i];
string targetFilenameNoFixup = installfile.base + SSEP + installfile.newname;
string targetFilename = MingwModuleHandler::PassThruCacheDirectory (
NormalizeFilename ( targetFilenameNoFixup ),
installDirectory );
out.push_back ( targetFilename );
}
}
void
MingwBackend::GetModuleInstallTargetFiles (
vector<string>& out ) const
{
for ( size_t i = 0; i < ProjectNode.modules.size (); i++ )
{
const Module& module = *ProjectNode.modules[i];
if ( module.installName.length () > 0 )
{
string targetFilenameNoFixup = module.installBase + SSEP + module.installName;
string targetFilename = MingwModuleHandler::PassThruCacheDirectory (
NormalizeFilename ( targetFilenameNoFixup ),
installDirectory );
out.push_back ( targetFilename );
}
}
}
void
MingwBackend::GetInstallTargetFiles (
vector<string>& out ) const
{
GetNonModuleInstallTargetFiles ( out );
GetModuleInstallTargetFiles ( out );
}
void
MingwBackend::OutputInstallTarget ( const string& sourceFilename,
const string& targetFilename,
const string& targetDirectory )
{
string normalizedTargetFilename = MingwModuleHandler::PassThruCacheDirectory (
NormalizeFilename ( targetDirectory + SSEP + targetFilename ),
installDirectory );
string normalizedTargetDirectory = MingwModuleHandler::PassThruCacheDirectory (
NormalizeFilename ( targetDirectory ),
installDirectory );
fprintf ( fMakefile,
"%s: %s %s\n",
normalizedTargetFilename.c_str (),
sourceFilename.c_str (),
normalizedTargetDirectory.c_str () );
fprintf ( fMakefile,
"\t$(ECHO_CP)\n" );
fprintf ( fMakefile,
"\t${cp} %s %s 1>$(NUL)\n",
sourceFilename.c_str (),
normalizedTargetFilename.c_str () );
}
void
MingwBackend::OutputNonModuleInstallTargets ()
{
for ( size_t i = 0; i < ProjectNode.installfiles.size (); i++ )
{
const InstallFile& installfile = *ProjectNode.installfiles[i];
OutputInstallTarget ( installfile.GetPath (),
installfile.newname,
installfile.base );
}
}
void
MingwBackend::OutputModuleInstallTargets ()
{
for ( size_t i = 0; i < ProjectNode.modules.size (); i++ )
{
const Module& module = *ProjectNode.modules[i];
if ( module.installName.length () > 0 )
{
string sourceFilename = MingwModuleHandler::PassThruCacheDirectory (
NormalizeFilename ( module.GetPath () ),
outputDirectory );
OutputInstallTarget ( sourceFilename,
module.installName,
module.installBase );
}
}
}
string
MingwBackend::GetRegistrySourceFiles ()
{
return "bootdata" SSEP "hivecls.inf "
"bootdata" SSEP "hivedef.inf "
"bootdata" SSEP "hiveinst.inf "
"bootdata" SSEP "hivesft.inf "
"bootdata" SSEP "hivesys.inf";
}
string
MingwBackend::GetRegistryTargetFiles ()
{
string system32ConfigDirectory = NormalizeFilename (
MingwModuleHandler::PassThruCacheDirectory (
"system32" SSEP "config" SSEP,
installDirectory ) );
return system32ConfigDirectory + SSEP "default " +
system32ConfigDirectory + SSEP "sam " +
system32ConfigDirectory + SSEP "security " +
system32ConfigDirectory + SSEP "software " +
system32ConfigDirectory + SSEP "system";
}
void
MingwBackend::OutputRegistryInstallTarget ()
{
string system32ConfigDirectory = NormalizeFilename (
MingwModuleHandler::PassThruCacheDirectory (
"system32" SSEP "config" SSEP,
installDirectory ) );
string registrySourceFiles = GetRegistrySourceFiles ();
string registryTargetFiles = GetRegistryTargetFiles ();
fprintf ( fMakefile,
"install_registry: %s\n",
registryTargetFiles.c_str () );
fprintf ( fMakefile,
"%s: %s %s $(MKHIVE_TARGET)\n",
registryTargetFiles.c_str (),
registrySourceFiles.c_str (),
system32ConfigDirectory.c_str () );
fprintf ( fMakefile,
"\t$(ECHO_MKHIVE)\n" );
fprintf ( fMakefile,
"\t$(MKHIVE_TARGET) bootdata %s bootdata" SSEP "hiveinst.inf\n",
system32ConfigDirectory.c_str () );
fprintf ( fMakefile,
"\n" );
}
void
MingwBackend::GenerateInstallTarget ()
{
vector<string> vInstallTargetFiles;
GetInstallTargetFiles ( vInstallTargetFiles );
string installTargetFiles = v2s ( vInstallTargetFiles, 5 );
fprintf ( fMakefile,
"install: %s install_registry\n",
installTargetFiles.c_str () );
OutputNonModuleInstallTargets ();
OutputModuleInstallTargets ();
OutputRegistryInstallTarget ();
fprintf ( fMakefile,
"\n" );
}