* Implement <autoregister>
[reactos.git] / reactos / tools / rbuild / backend / mingw / mingw.cpp
index 11e022f..7b4ceb3 100644 (file)
@@ -1,16 +1,30 @@
-
+/*
+ * Copyright (C) 2005 Casper S. Hornstrup
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
 #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
+#ifdef _MSC_VER
+#define popen _popen
+#define pclose _pclose
+#endif//_MSC_VER
 
 using std::string;
 using std::vector;
@@ -41,195 +55,27 @@ v2s ( const string_list& v, int wrap_at )
 }
 
 
-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;
-}
-
-void
-Directory::ResolveVariablesInPath ( char* buf,
-                                    string path )
-{
-       string s = ReplaceVariable ( "$(INTERMEDIATE)", Environment::GetIntermediatePath (), path );
-       s = ReplaceVariable ( "$(OUTPUT)", Environment::GetOutputPath (), s );
-       s = ReplaceVariable ( "$(INSTALL)", Environment::GetInstallPath (), s );
-       strcpy ( buf, s.c_str () );
-}
-
-void
-Directory::GenerateTree ( const string& parent,
-                          bool verbose )
-{
-       string path;
-
-       if ( parent.size () > 0 )
-       {
-               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 );
-       }
-}
-
-string
-Directory::EscapeSpaces ( string path )
-{
-       string newpath;
-       char* p = &path[0];
-       while ( *p != 0 )
-       {
-               if ( *p == ' ' )
-                       newpath = newpath + "\\ ";
-               else
-                       newpath = newpath + *p;
-               *p++;
-       }
-       return newpath;
-}
-
-void
-Directory::CreateRule ( FILE* f,
-                        const string& parent )
-{
-       string path;
-
-       if ( parent.size() > 0 )
-       {
-               string escapedParent = EscapeSpaces ( parent );
-               fprintf ( f,
-                       "%s%c%s: | %s\n",
-                       escapedParent.c_str (),
-                       CSEP,
-                       EscapeSpaces ( name ).c_str (),
-                       escapedParent.c_str () );
-
-               fprintf ( f,
-                       "\t$(ECHO_MKDIR)\n" );
-
-               fprintf ( f,
-                       "\t${mkdir} $@\n" );
-
-               path = parent + SSEP + name;
-       }
-       else
-               path = name;
-
-       for ( directory_map::iterator i = subdirs.begin();
-               i != subdirs.end();
-               ++i )
-       {
-               i->second->CreateRule ( f, path );
-       }
-}
-
-
 static class MingwFactory : public Backend::Factory
 {
 public:
        MingwFactory() : Factory ( "mingw" ) {}
        Backend* operator() ( Project& project,
-                             bool verbose,
-                             bool cleanAsYouGo )
+                             Configuration& configuration )
        {
                return new MingwBackend ( project,
-                                         verbose,
-                                         cleanAsYouGo );
+                                         configuration );
        }
 } factory;
 
 
 MingwBackend::MingwBackend ( Project& project,
-                             bool verbose,
-                             bool cleanAsYouGo )
-       : Backend ( project, verbose, cleanAsYouGo ),
+                             Configuration& configuration )
+       : Backend ( project, configuration ),
          intermediateDirectory ( new Directory ("$(INTERMEDIATE)" ) ),
          outputDirectory ( new Directory ( "$(OUTPUT)" ) ),
          installDirectory ( new Directory ( "$(INSTALL)" ) )
 {
+       compilerPrefix = "";
 }
 
 MingwBackend::~MingwBackend()
@@ -248,6 +94,42 @@ MingwBackend::AddDirectoryTarget ( const string& directory,
        return directoryTree->name;
 }
 
+bool
+MingwBackend::CanEnablePreCompiledHeaderSupportForModule ( const Module& module )
+{
+       if ( !configuration.CompilationUnitsEnabled )
+               return true;
+
+       const vector<CompilationUnit*>& compilationUnits = module.non_if_data.compilationUnits;
+       size_t i;
+       for ( i = 0; i < compilationUnits.size (); i++ )
+       {
+               CompilationUnit& compilationUnit = *compilationUnits[i];
+               if ( compilationUnit.files.size () != 1 )
+                       return false;
+       }
+       // intentionally make a copy so that we can append more work in
+       // the middle of processing without having to go recursive
+       vector<If*> v = module.non_if_data.ifs;
+       for ( i = 0; i < v.size (); i++ )
+       {
+               size_t j;
+               If& rIf = *v[i];
+               // check for sub-ifs to add to list
+               const vector<If*>& ifs = rIf.data.ifs;
+               for ( j = 0; j < ifs.size (); j++ )
+                       v.push_back ( ifs[j] );
+               const vector<CompilationUnit*>& compilationUnits = rIf.data.compilationUnits;
+               for ( j = 0; j < compilationUnits.size (); j++ )
+               {
+                       CompilationUnit& compilationUnit = *compilationUnits[j];
+                       if ( compilationUnit.files.size () != 1 )
+                               return false;
+               }
+       }
+       return true;
+}
+
 void
 MingwBackend::ProcessModules ()
 {
@@ -258,9 +140,13 @@ MingwBackend::ProcessModules ()
        for ( i = 0; i < ProjectNode.modules.size (); i++ )
        {
                Module& module = *ProjectNode.modules[i];
+               if ( !module.enabled )
+                       continue;
                MingwModuleHandler* h = MingwModuleHandler::InstanciateHandler (
                        module,
                        this );
+               if ( use_pch && CanEnablePreCompiledHeaderSupportForModule ( module ) )
+                       h->EnablePreCompiledHeaderSupport ();
                if ( module.host == HostDefault )
                {
                        module.host = h->DefaultHost();
@@ -293,16 +179,50 @@ MingwBackend::ProcessModules ()
                h.GenerateInvocations ();
                h.GenerateCleanTarget ();
                h.GenerateInstallTarget ();
+               h.GenerateDependsTarget ();
                delete v[i];
        }
 
        printf ( "done\n" );
 }
-       
+
 void
 MingwBackend::Process ()
+{
+       if ( configuration.CheckDependenciesForModuleOnly )
+               CheckAutomaticDependenciesForModuleOnly ();
+       else
+               ProcessNormal ();
+}
+
+void
+MingwBackend::CheckAutomaticDependenciesForModuleOnly ()
+{
+       if ( configuration.AutomaticDependencies )
+       {
+               Module* module = ProjectNode.LocateModule ( configuration.CheckDependenciesForModuleOnlyModule );
+               if ( module == NULL )
+               {
+                       printf ( "Module '%s' does not exist\n",
+                               configuration.CheckDependenciesForModuleOnlyModule.c_str () );
+                       return;
+               }
+               
+               printf ( "Checking automatic dependencies for module '%s'...",
+                        module->name.c_str () );
+               AutomaticDependency automaticDependency ( ProjectNode );
+               automaticDependency.CheckAutomaticDependenciesForModule ( *module,
+                                                                         configuration.Verbose );
+               printf ( "done\n" );
+       }
+}
+
+void
+MingwBackend::ProcessNormal ()
 {
        DetectCompiler ();
+       DetectBinutils ();
+       DetectNetwideAssembler ();
        DetectPipeSupport ();
        DetectPCHSupport ();
        CreateMakefile ();
@@ -311,10 +231,13 @@ MingwBackend::Process ()
        GenerateXmlBuildFilesMacro ();
        ProcessModules ();
        GenerateInstallTarget ();
+       GenerateTestTarget ();
        GenerateDirectoryTargets ();
        GenerateDirectories ();
        UnpackWineResources ();
        GenerateTestSupportCode ();
+       GenerateCompilationUnitSupportCode ();
+       GenerateSysSetup ();
        GenerateProxyMakefiles ();
        CheckAutomaticDependencies ();
        CloseMakefile ();
@@ -328,7 +251,6 @@ MingwBackend::CreateMakefile ()
                throw AccessDeniedException ( ProjectNode.makefile );
        MingwModuleHandler::SetBackend ( this );
        MingwModuleHandler::SetMakefile ( fMakefile );
-       MingwModuleHandler::SetUsePch ( use_pch );
 }
 
 void
@@ -484,6 +406,13 @@ MingwBackend::GenerateProjectLFLAGS () const
 void
 MingwBackend::GenerateGlobalVariables () const
 {
+       fprintf ( fMakefile,
+                 "PREFIX := %s\n",
+                 compilerPrefix.c_str () );
+       fprintf ( fMakefile,
+                 "nasm := %s\n",
+                 nasmCommand.c_str () );
+
        GenerateGlobalCFlagsAndProperties ( "=", ProjectNode.non_if_data );
        GenerateProjectGccOptions ( "=", ProjectNode.non_if_data );
 
@@ -491,8 +420,8 @@ MingwBackend::GenerateGlobalVariables () const
        fprintf ( fMakefile, "PROJECT_WIDLFLAGS := $(PROJECT_CFLAGS)\n" );
        fprintf ( fMakefile, "PROJECT_LFLAGS := %s\n",
                  GenerateProjectLFLAGS ().c_str () );
-       fprintf ( fMakefile, "PROJECT_CFLAGS += $(PROJECT_GCCOPTIONS)\n" );
        fprintf ( fMakefile, "PROJECT_CFLAGS += -Wall\n" );
+       fprintf ( fMakefile, "PROJECT_CFLAGS += $(PROJECT_GCCOPTIONS)\n" );
        fprintf ( fMakefile, "\n" );
 }
 
@@ -509,6 +438,8 @@ MingwBackend::IncludeInAllTarget ( const Module& module ) const
                return false;
        if ( module.type == Test )
                return false;
+       if ( module.type == Alias )
+               return false;
        return true;
 }
 
@@ -540,6 +471,8 @@ MingwBackend::GetBuildToolDependencies () const
        for ( size_t i = 0; i < ProjectNode.modules.size (); i++ )
        {
                Module& module = *ProjectNode.modules[i];
+               if ( !module.enabled )
+                       continue;
                if ( module.type == BuildTool )
                {
                        if ( dependencies.length () > 0 )
@@ -565,7 +498,7 @@ MingwBackend::GenerateRegTestsRunTarget () const
        fprintf ( fMakefile,
                  "REGTESTS_RUN_TARGET = regtests.dll\n" );
        fprintf ( fMakefile,
-                 "$(REGTESTS_RUN_TARGET):\n" );
+                 "$(REGTESTS_RUN_TARGET): $(REGTESTS_TARGET)\n" );
        fprintf ( fMakefile,
                  "\t$(cp) $(REGTESTS_TARGET) $(REGTESTS_RUN_TARGET)\n" );
        fprintf ( fMakefile, "\n" );
@@ -600,8 +533,7 @@ MingwBackend::GenerateXmlBuildFilesMacro() const
                        else
                        {
                                fprintf ( fMakefile,
-                                         " \\\n",
-                                         xmlbuildFilenames.c_str () );
+                                         " \\\n" );
                        }
                        xmlbuildFilenames.resize ( 0 );
                }
@@ -613,7 +545,7 @@ MingwBackend::GenerateXmlBuildFilesMacro() const
 string
 MingwBackend::GetBin2ResExecutable ()
 {
-       return NormalizeFilename ( Environment::GetOutputPath () + SSEP + "tools/bin2res/bin2res" + EXEPOSTFIX );
+       return NormalizeFilename ( Environment::GetOutputPath () + sSep + "tools/bin2res/bin2res" + ExePostfix );
 }
 
 void
@@ -622,7 +554,7 @@ MingwBackend::UnpackWineResources ()
        printf ( "Unpacking WINE resources..." );
        WineResource wineResource ( ProjectNode,
                                    GetBin2ResExecutable () );
-       wineResource.UnpackResources ( verbose );
+       wineResource.UnpackResources ( configuration.Verbose );
        printf ( "done\n" );
 }
 
@@ -631,33 +563,66 @@ MingwBackend::GenerateTestSupportCode ()
 {
        printf ( "Generating test support code..." );
        TestSupportCode testSupportCode ( ProjectNode );
-       testSupportCode.GenerateTestSupportCode ( verbose );
+       testSupportCode.GenerateTestSupportCode ( configuration.Verbose );
        printf ( "done\n" );
 }
 
+void
+MingwBackend::GenerateCompilationUnitSupportCode ()
+{
+       if ( configuration.CompilationUnitsEnabled )
+       {
+               printf ( "Generating compilation unit support code..." );
+               CompilationUnitSupportCode compilationUnitSupportCode ( ProjectNode );
+               compilationUnitSupportCode.Generate ( configuration.Verbose );
+               printf ( "done\n" );
+       }
+}
+
+void
+MingwBackend::GenerateSysSetup ()
+{
+       printf ( "Generating syssetup.inf..." );
+       SysSetupGenerator sysSetupGenerator ( ProjectNode );
+       sysSetupGenerator.Generate ();
+       printf ( "done\n" );
+}
+
+string
+MingwBackend::GetProxyMakefileTree () const
+{
+       if ( configuration.GenerateProxyMakefilesInSourceTree )
+               return "";
+       else
+               return Environment::GetOutputPath ();
+}
+
 void
 MingwBackend::GenerateProxyMakefiles ()
 {
        printf ( "Generating proxy makefiles..." );
        ProxyMakefile proxyMakefile ( ProjectNode );
-       proxyMakefile.GenerateProxyMakefiles ( verbose );
+       proxyMakefile.GenerateProxyMakefiles ( configuration.Verbose,
+                                              GetProxyMakefileTree () );
        printf ( "done\n" );
 }
 
 void
 MingwBackend::CheckAutomaticDependencies ()
 {
-       printf ( "Checking automatic dependencies..." );
-       AutomaticDependency automaticDependency ( ProjectNode );
-       automaticDependency.Process ();
-       automaticDependency.CheckAutomaticDependencies ( verbose );
-       printf ( "done\n" );
+       if ( configuration.AutomaticDependencies )
+       {
+               printf ( "Checking automatic dependencies..." );
+               AutomaticDependency automaticDependency ( ProjectNode );
+               automaticDependency.CheckAutomaticDependencies ( configuration.Verbose );
+               printf ( "done\n" );
+       }
 }
 
 bool
 MingwBackend::IncludeDirectoryTarget ( const string& directory ) const
 {
-       if ( directory == "$(INTERMEDIATE)" SSEP "tools")
+       if ( directory == "$(INTERMEDIATE)" + sSep + "tools")
                return false;
        else
                return true;
@@ -667,9 +632,10 @@ void
 MingwBackend::GenerateDirectories ()
 {
        printf ( "Creating directories..." );
-       intermediateDirectory->GenerateTree ( "", verbose );
-       outputDirectory->GenerateTree ( "", verbose );
-       installDirectory->GenerateTree ( "", verbose );
+       intermediateDirectory->GenerateTree ( "", configuration.Verbose );
+       outputDirectory->GenerateTree ( "", configuration.Verbose );
+       if ( !configuration.MakeHandlesInstallDirectories )
+               installDirectory->GenerateTree ( "", configuration.Verbose );
        printf ( "done\n" );
 }
 
@@ -677,8 +643,9 @@ bool
 MingwBackend::TryToDetectThisCompiler ( const string& compiler )
 {
        string command = ssprintf (
-               "%s -v 2>%s",
-               compiler.c_str (),
+               "%s -v 1>%s 2>%s",
+               FixSeparatorForSystemCommand(compiler).c_str (),
+               NUL,
                NUL );
        int exitcode = system ( command.c_str () );
        return (exitcode == 0);
@@ -693,19 +660,22 @@ MingwBackend::DetectCompiler ()
        const string& ROS_PREFIXValue = Environment::GetVariable ( "ROS_PREFIX" );
        if ( ROS_PREFIXValue.length () > 0 )
        {
-               compilerCommand = ROS_PREFIXValue + "-gcc";
+               compilerPrefix = ROS_PREFIXValue;
+               compilerCommand = compilerPrefix + "-gcc";
                detectedCompiler = TryToDetectThisCompiler ( compilerCommand );
        }
 #if defined(WIN32)
        if ( !detectedCompiler )
        {
+               compilerPrefix = "";
                compilerCommand = "gcc";
                detectedCompiler = TryToDetectThisCompiler ( compilerCommand );
        }
 #endif
        if ( !detectedCompiler )
        {
-               compilerCommand = "mingw32-gcc";
+               compilerPrefix = "mingw32";
+               compilerCommand = compilerPrefix + "-gcc";
                detectedCompiler = TryToDetectThisCompiler ( compilerCommand );
        }
        if ( detectedCompiler )
@@ -714,19 +684,165 @@ MingwBackend::DetectCompiler ()
                printf ( "not detected\n" );
 }
 
+bool
+MingwBackend::TryToDetectThisNetwideAssembler ( const string& assembler )
+{
+       string command = ssprintf (
+               "%s -h 1>%s 2>%s",
+               FixSeparatorForSystemCommand(assembler).c_str (),
+               NUL,
+               NUL );
+       int exitcode = system ( command.c_str () );
+       return (exitcode == 0);
+}
+
+bool
+MingwBackend::TryToDetectThisBinutils ( const string& binutils )
+{
+       string command = ssprintf (
+               "%s -v 1>%s",
+               FixSeparatorForSystemCommand(binutils).c_str (),
+               NUL,
+               NUL );
+       int exitcode = system ( command.c_str () );
+       return (exitcode == 0);
+}
+
+string
+MingwBackend::GetBinutilsVersion ( const string& binutilsCommand )
+{
+       FILE *fp;
+       int ch, i;
+       char buffer[81];
+
+       string versionCommand = ssprintf ( "%s -v",
+                                          binutilsCommand.c_str (),
+                                          NUL,
+                                          NUL );
+       fp = popen ( versionCommand.c_str () , "r" );
+       for( i = 0; 
+             ( i < 80 ) && 
+                 ( feof ( fp ) == 0 && 
+                   ( ( ch = fgetc( fp ) ) != -1 ) ); 
+             i++ )
+       {
+               buffer[i] = (char) ch;
+       }
+       buffer[i] = '\0';
+       pclose ( fp );
+       
+       char separators[] = " ";
+       char *token;
+       char *prevtoken = NULL;
+       
+       token = strtok ( buffer, separators );
+       while ( token != NULL )
+       {
+               prevtoken = token;
+               token = strtok ( NULL, separators );
+       }
+       string version = string ( prevtoken );
+       int lastDigit = version.find_last_not_of ( "\t\r\n" );
+       if ( lastDigit != -1 )
+               return string ( version, 0, lastDigit+1 );
+       else
+               return version;
+}
+
+bool
+MingwBackend::IsSupportedBinutilsVersion ( const string& binutilsVersion )
+{
+       if ( ( ( strcmp ( binutilsVersion.c_str (), "20040902") >= 0 ) &&
+              ( strcmp ( binutilsVersion.c_str (), "20041008") <= 0 ) ) ||
+              ( strcmp ( binutilsVersion.c_str (), "20031001") < 0 ) )
+               return false;
+       else
+               return true;
+}
+
+void
+MingwBackend::DetectBinutils ()
+{
+       printf ( "Detecting binutils..." );
+
+       bool detectedBinutils = false;
+       const string& ROS_PREFIXValue = Environment::GetVariable ( "ROS_PREFIX" );
+       if ( ROS_PREFIXValue.length () > 0 )
+       {
+               binutilsPrefix = ROS_PREFIXValue;
+               binutilsCommand = binutilsPrefix + "-ld";
+               detectedBinutils = TryToDetectThisBinutils ( binutilsCommand );
+       }
+#if defined(WIN32)
+       if ( !detectedBinutils )
+       {
+               binutilsPrefix = "";
+               binutilsCommand = "ld";
+               detectedBinutils = TryToDetectThisBinutils ( binutilsCommand );
+       }
+#endif
+       if ( !detectedBinutils )
+       {
+               binutilsPrefix = "mingw32";
+               binutilsCommand = binutilsPrefix + "-ld";
+               detectedBinutils = TryToDetectThisBinutils ( binutilsCommand );
+       }
+       if ( detectedBinutils )
+       {
+               string binutilsVersion = GetBinutilsVersion ( binutilsCommand );
+               if ( IsSupportedBinutilsVersion ( binutilsVersion ) )
+                       printf ( "detected (%s)\n", binutilsCommand.c_str () );
+               else
+               {
+                       printf ( "detected (%s), but with unsupported version (%s)\n",
+                                binutilsCommand.c_str (),
+                                binutilsVersion.c_str () );
+                       throw UnsupportedBuildToolException ( binutilsCommand, binutilsVersion );
+               }
+       }
+       else
+               printf ( "not detected\n" );
+}
+
+void
+MingwBackend::DetectNetwideAssembler ()
+{
+       printf ( "Detecting netwide assembler..." );
+
+       nasmCommand = "nasm";
+       bool detectedNasm = TryToDetectThisNetwideAssembler ( nasmCommand );
+#if defined(WIN32)
+       if ( !detectedNasm )
+       {
+               nasmCommand = "nasmw";
+               detectedNasm = TryToDetectThisNetwideAssembler ( nasmCommand );
+       }
+#endif
+       if ( !detectedNasm )
+       {
+               nasmCommand = "yasm";
+               detectedNasm = TryToDetectThisNetwideAssembler ( nasmCommand );
+       }
+       if ( detectedNasm )
+               printf ( "detected (%s)\n", nasmCommand.c_str () );
+       else
+               printf ( "not detected\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_detection = "tools" + sSep + "rbuild" + sSep + "backend" + sSep + "mingw" + sSep + "pipe_detection.c";
        string pipe_detectionObjectFilename = ReplaceExtension ( pipe_detection,
                                                                 ".o" );
        string command = ssprintf (
-               "%s -pipe -c %s -o %s 2>%s",
-               compilerCommand.c_str (),
+               "%s -pipe -c %s -o %s 1>%s 2>%s",
+               FixSeparatorForSystemCommand(compilerCommand).c_str (),
                pipe_detection.c_str (),
                pipe_detectionObjectFilename.c_str (),
+               NUL,
                NUL );
        int exitcode = system ( command.c_str () );
        FILE* f = fopen ( pipe_detectionObjectFilename.c_str (), "rb" );
@@ -750,11 +866,12 @@ MingwBackend::DetectPCHSupport ()
 {
        printf ( "Detecting compiler pre-compiled header support..." );
 
-       string path = "tools" SSEP "rbuild" SSEP "backend" SSEP "mingw" SSEP "pch_detection.h";
+       string path = "tools" + sSep + "rbuild" + sSep + "backend" + sSep + "mingw" + sSep + "pch_detection.h";
        string cmd = ssprintf (
-               "%s -c %s 2>%s",
-               compilerCommand.c_str (),
+               "%s -c %s 1>%s 2>%s",
+               FixSeparatorForSystemCommand(compilerCommand).c_str (),
                path.c_str (),
+               NUL,
                NUL );
        system ( cmd.c_str () );
        path += ".gch";
@@ -782,7 +899,7 @@ MingwBackend::GetNonModuleInstallTargetFiles (
        for ( size_t i = 0; i < ProjectNode.installfiles.size (); i++ )
        {
                const InstallFile& installfile = *ProjectNode.installfiles[i];
-               string targetFilenameNoFixup = installfile.base + SSEP + installfile.newname;
+               string targetFilenameNoFixup = installfile.base + sSep + installfile.newname;
                string targetFilename = MingwModuleHandler::PassThruCacheDirectory (
                        NormalizeFilename ( targetFilenameNoFixup ),
                        installDirectory );
@@ -797,11 +914,13 @@ MingwBackend::GetModuleInstallTargetFiles (
        for ( size_t i = 0; i < ProjectNode.modules.size (); i++ )
        {
                const Module& module = *ProjectNode.modules[i];
+               if ( !module.enabled )
+                       continue;
                if ( module.installName.length () > 0 )
                {
                        string targetFilenameNoFixup;
                        if ( module.installBase.length () > 0 )
-                               targetFilenameNoFixup = module.installBase + SSEP + module.installName;
+                               targetFilenameNoFixup = module.installBase + sSep + module.installName;
                        else
                                targetFilenameNoFixup = module.installName;
                        string targetFilename = MingwModuleHandler::PassThruCacheDirectory (
@@ -827,7 +946,7 @@ MingwBackend::OutputInstallTarget ( const string& sourceFilename,
 {
        string fullTargetFilename;
        if ( targetDirectory.length () > 0)
-               fullTargetFilename = targetDirectory + SSEP + targetFilename;
+               fullTargetFilename = targetDirectory + sSep + targetFilename;
        else
                fullTargetFilename = targetFilename;
        string normalizedTargetFilename = MingwModuleHandler::PassThruCacheDirectory (
@@ -861,20 +980,36 @@ MingwBackend::OutputNonModuleInstallTargets ()
        }
 }
 
+const Module&
+MingwBackend::GetAliasedModuleOrModule ( const Module& module ) const
+{
+       if ( module.aliasedModuleName.size () > 0 )
+       {
+               const Module* aliasedModule = ProjectNode.LocateModule ( module.aliasedModuleName );
+               assert ( aliasedModule );
+               return *aliasedModule;
+       }
+       else
+               return module;
+}
+
 void
 MingwBackend::OutputModuleInstallTargets ()
 {
        for ( size_t i = 0; i < ProjectNode.modules.size (); i++ )
        {
                const Module& module = *ProjectNode.modules[i];
+               if ( !module.enabled )
+                       continue;
                if ( module.installName.length () > 0 )
                {
+                       const Module& aliasedModule = GetAliasedModuleOrModule ( module );
                        string sourceFilename = MingwModuleHandler::PassThruCacheDirectory (
-                               NormalizeFilename ( module.GetPath () ),
+                               NormalizeFilename ( aliasedModule.GetPath () ),
                                outputDirectory );
                        OutputInstallTarget ( sourceFilename,
-                                         module.installName,
-                                         module.installBase );
+                                             module.installName,
+                                             module.installBase );
                }
        }
 }
@@ -882,11 +1017,11 @@ MingwBackend::OutputModuleInstallTargets ()
 string
 MingwBackend::GetRegistrySourceFiles ()
 {
-       return "bootdata" SSEP "hivecls.inf "
-               "bootdata" SSEP "hivedef.inf "
-               "bootdata" SSEP "hiveinst.inf "
-               "bootdata" SSEP "hivesft.inf "
-               "bootdata" SSEP "hivesys.inf";
+       return "bootdata" + sSep + "hivecls.inf "
+               "bootdata" + sSep + "hivedef.inf "
+               "bootdata" + sSep + "hiveinst.inf "
+               "bootdata" + sSep + "hivesft.inf "
+               "bootdata" + sSep + "hivesys.inf";
 }
 
 string
@@ -894,13 +1029,13 @@ MingwBackend::GetRegistryTargetFiles ()
 {
        string system32ConfigDirectory = NormalizeFilename (
                MingwModuleHandler::PassThruCacheDirectory (
-               "system32" SSEP "config" SSEP,
+               "system32" + sSep + "config" + sSep,
                installDirectory ) );
-       return system32ConfigDirectory + SSEP "default " +
-               system32ConfigDirectory + SSEP "sam " +
-               system32ConfigDirectory + SSEP "security " +
-               system32ConfigDirectory + SSEP "software " +
-               system32ConfigDirectory + SSEP "system";
+       return system32ConfigDirectory + sSep + "default " +
+               system32ConfigDirectory + sSep + "sam " +
+                system32ConfigDirectory + sSep + "security " +
+               system32ConfigDirectory + sSep + "software " +
+               system32ConfigDirectory + sSep + "system";
 }
 
 void
@@ -908,7 +1043,7 @@ MingwBackend::OutputRegistryInstallTarget ()
 {
        string system32ConfigDirectory = NormalizeFilename (
                MingwModuleHandler::PassThruCacheDirectory (
-               "system32" SSEP "config" SSEP,
+               "system32" + sSep + "config" + sSep,
                installDirectory ) );
 
        string registrySourceFiles = GetRegistrySourceFiles ();
@@ -924,8 +1059,9 @@ MingwBackend::OutputRegistryInstallTarget ()
        fprintf ( fMakefile,
                  "\t$(ECHO_MKHIVE)\n" );
        fprintf ( fMakefile,
-                 "\t$(MKHIVE_TARGET) bootdata %s bootdata" SSEP "hiveinst.inf\n",
-                 system32ConfigDirectory.c_str () );
+                 "\t$(MKHIVE_TARGET) bootdata %s bootdata%chiveinst.inf\n",
+                 system32ConfigDirectory.c_str (),
+                  cSep );
        fprintf ( fMakefile,
                  "\n" );
 }
@@ -949,6 +1085,34 @@ MingwBackend::GenerateInstallTarget ()
                  "\n" );
 }
 
+void
+MingwBackend::GetModuleTestTargets (
+       vector<string>& out ) const
+{
+       for ( size_t i = 0; i < ProjectNode.modules.size (); i++ )
+       {
+               const Module& module = *ProjectNode.modules[i];
+               if ( !module.enabled )
+                       continue;
+               if ( module.type == Test )
+                       out.push_back ( module.name );
+       }
+}
+
+void
+MingwBackend::GenerateTestTarget ()
+{
+       vector<string> vTestTargets;
+       GetModuleTestTargets ( vTestTargets );
+       string testTargets = v2s ( vTestTargets, 5 );
+
+       fprintf ( fMakefile,
+                 "test: %s\n",
+                 testTargets.c_str () );
+       fprintf ( fMakefile,
+                 "\n" );
+}
+
 void
 MingwBackend::GenerateDirectoryTargets ()
 {