* Implement <autoregister>
[reactos.git] / reactos / tools / rbuild / backend / mingw / mingw.cpp
index 0a02169..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,188 +55,27 @@ v2s ( const string_list& v, int wrap_at )
 }
 
 
-/* static */ string
-Environment::GetVariable ( const string& name )
-{
-       char* value = getenv ( name.c_str () );
-       if ( value != NULL && strlen ( value ) > 0 )
-               return ssprintf ( "%s",
-                                 value );
-       else
-               return "";
-}
-
-
-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::GetEnvironmentVariablePathOrDefault ( const string& name,
-                                                    const string& defaultValue )
-{
-       const string& environmentVariableValue = Environment::GetVariable ( name );
-       if ( environmentVariableValue.length () > 0 )
-               return NormalizeFilename ( environmentVariableValue );
-       else
-               return defaultValue;
-}
-
-string
-Directory::GetIntermediatePath ()
-{
-       return GetEnvironmentVariablePathOrDefault ( "ROS_INTERMEDIATE",
-                                                    "obj-i386" );
-}
-
-string
-Directory::GetOutputPath ()
-{
-       return GetEnvironmentVariablePathOrDefault ( "ROS_OUTPUT",
-                                                    "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,
-                             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()
@@ -234,12 +87,49 @@ MingwBackend::~MingwBackend()
 
 string
 MingwBackend::AddDirectoryTarget ( const string& directory,
-                                      Directory* directoryTree )
+                                   Directory* directoryTree )
 {
-       directoryTree->Add ( directory.c_str() );
+       if ( directory.length () > 0)
+               directoryTree->Add ( directory.c_str() );
        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 ()
 {
@@ -250,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();
@@ -272,6 +166,7 @@ MingwBackend::ProcessModules ()
 
        GenerateAllTarget ( v );
        GenerateInitTarget ();
+       GenerateRegTestsRunTarget ();
 
        for ( i = 0; i < iend; i++ )
                v[i]->GenerateOtherMacros();
@@ -284,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 ();
@@ -302,7 +231,14 @@ MingwBackend::Process ()
        GenerateXmlBuildFilesMacro ();
        ProcessModules ();
        GenerateInstallTarget ();
+       GenerateTestTarget ();
+       GenerateDirectoryTargets ();
        GenerateDirectories ();
+       UnpackWineResources ();
+       GenerateTestSupportCode ();
+       GenerateCompilationUnitSupportCode ();
+       GenerateSysSetup ();
+       GenerateProxyMakefiles ();
        CheckAutomaticDependencies ();
        CloseMakefile ();
 }
@@ -315,7 +251,6 @@ MingwBackend::CreateMakefile ()
                throw AccessDeniedException ( ProjectNode.makefile );
        MingwModuleHandler::SetBackend ( this );
        MingwModuleHandler::SetMakefile ( fMakefile );
-       MingwModuleHandler::SetUsePch ( use_pch );
 }
 
 void
@@ -331,37 +266,27 @@ MingwBackend::GenerateHeader () const
        fprintf ( fMakefile, "# THIS FILE IS AUTOMATICALLY GENERATED, EDIT 'ReactOS.xml' INSTEAD\n\n" );
 }
 
+string
+MingwBackend::GenerateIncludesAndDefines ( IfableData& data ) const
+{
+       string includeParameters = MingwModuleHandler::GenerateGccIncludeParametersFromVector ( data.includes );
+       string defineParameters = MingwModuleHandler::GenerateGccDefineParametersFromVector ( data.defines );
+       return includeParameters + " " + defineParameters;
+}
+
 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() );
-       }
+       
+       fprintf ( fMakefile,
+                 " %s",
+                 GenerateIncludesAndDefines ( data ).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" );
 }
 
@@ -408,6 +333,62 @@ MingwBackend::GenerateGlobalCFlagsAndProperties (
        }
 }
 
+void
+MingwBackend::GenerateProjectGccOptionsMacro ( const char* assignmentOperation,
+                                               IfableData& data ) const
+{
+       size_t i;
+
+       fprintf (
+               fMakefile,
+               "PROJECT_GCCOPTIONS %s",
+               assignmentOperation );
+       
+       for ( i = 0; i < data.compilerFlags.size(); i++ )
+       {
+               fprintf (
+                       fMakefile,
+                       " %s",
+                       data.compilerFlags[i]->flag.c_str() );
+       }
+
+       fprintf ( fMakefile, "\n" );
+}
+
+void
+MingwBackend::GenerateProjectGccOptions (
+       const char* assignmentOperation,
+       IfableData& data ) const
+{
+       size_t i;
+
+       if ( data.compilerFlags.size() )
+       {
+               GenerateProjectGccOptionsMacro ( assignmentOperation,
+                                                data );
+       }
+
+       for ( i = 0; i < data.ifs.size(); i++ )
+       {
+               If& rIf = *data.ifs[i];
+               if ( rIf.data.compilerFlags.size()
+                    || rIf.data.ifs.size() )
+               {
+                       fprintf (
+                               fMakefile,
+                               "ifeq (\"$(%s)\",\"%s\")\n",
+                               rIf.property.c_str(),
+                               rIf.value.c_str() );
+                       GenerateProjectGccOptions (
+                               "+=",
+                               rIf.data );
+                       fprintf (
+                               fMakefile,
+                               "endif\n\n" );
+               }
+       }
+}
+
 string
 MingwBackend::GenerateProjectLFLAGS () const
 {
@@ -425,15 +406,22 @@ MingwBackend::GenerateProjectLFLAGS () const
 void
 MingwBackend::GenerateGlobalVariables () const
 {
-       GenerateGlobalCFlagsAndProperties (
-               "=",
-               ProjectNode.non_if_data );
+       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 );
 
        fprintf ( fMakefile, "PROJECT_RCFLAGS := $(PROJECT_CFLAGS)\n" );
        fprintf ( fMakefile, "PROJECT_WIDLFLAGS := $(PROJECT_CFLAGS)\n" );
        fprintf ( fMakefile, "PROJECT_LFLAGS := %s\n",
                  GenerateProjectLFLAGS ().c_str () );
        fprintf ( fMakefile, "PROJECT_CFLAGS += -Wall\n" );
+       fprintf ( fMakefile, "PROJECT_CFLAGS += $(PROJECT_GCCOPTIONS)\n" );
        fprintf ( fMakefile, "\n" );
 }
 
@@ -450,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;
 }
 
@@ -481,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 )
@@ -500,6 +492,18 @@ MingwBackend::GenerateInitTarget () const
        fprintf ( fMakefile, "\n" );
 }
 
+void
+MingwBackend::GenerateRegTestsRunTarget () const
+{
+       fprintf ( fMakefile,
+                 "REGTESTS_RUN_TARGET = regtests.dll\n" );
+       fprintf ( fMakefile,
+                 "$(REGTESTS_RUN_TARGET): $(REGTESTS_TARGET)\n" );
+       fprintf ( fMakefile,
+                 "\t$(cp) $(REGTESTS_TARGET) $(REGTESTS_RUN_TARGET)\n" );
+       fprintf ( fMakefile, "\n" );
+}
+
 void
 MingwBackend::GenerateXmlBuildFilesMacro() const
 {
@@ -529,8 +533,7 @@ MingwBackend::GenerateXmlBuildFilesMacro() const
                        else
                        {
                                fprintf ( fMakefile,
-                                         " \\\n",
-                                         xmlbuildFilenames.c_str () );
+                                         " \\\n" );
                        }
                        xmlbuildFilenames.resize ( 0 );
                }
@@ -539,20 +542,87 @@ MingwBackend::GenerateXmlBuildFilesMacro() const
        fprintf ( fMakefile, "\n" );
 }
 
+string
+MingwBackend::GetBin2ResExecutable ()
+{
+       return NormalizeFilename ( Environment::GetOutputPath () + sSep + "tools/bin2res/bin2res" + ExePostfix );
+}
+
 void
-MingwBackend::CheckAutomaticDependencies ()
+MingwBackend::UnpackWineResources ()
+{
+       printf ( "Unpacking WINE resources..." );
+       WineResource wineResource ( ProjectNode,
+                                   GetBin2ResExecutable () );
+       wineResource.UnpackResources ( configuration.Verbose );
+       printf ( "done\n" );
+}
+
+void
+MingwBackend::GenerateTestSupportCode ()
+{
+       printf ( "Generating test support code..." );
+       TestSupportCode testSupportCode ( ProjectNode );
+       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 ( "Checking automatic dependencies..." );
-       AutomaticDependency automaticDependency ( ProjectNode );
-       automaticDependency.Process ();
-       automaticDependency.CheckAutomaticDependencies ( verbose );
+       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 ( configuration.Verbose,
+                                              GetProxyMakefileTree () );
+       printf ( "done\n" );
+}
+
+void
+MingwBackend::CheckAutomaticDependencies ()
+{
+       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;
@@ -562,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" );
 }
 
@@ -572,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);
@@ -588,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 )
@@ -609,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" );
@@ -645,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";
@@ -677,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 );
@@ -692,9 +914,15 @@ 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 = module.installBase + SSEP + module.installName;
+                       string targetFilenameNoFixup;
+                       if ( module.installBase.length () > 0 )
+                               targetFilenameNoFixup = module.installBase + sSep + module.installName;
+                       else
+                               targetFilenameNoFixup = module.installName;
                        string targetFilename = MingwModuleHandler::PassThruCacheDirectory (
                                NormalizeFilename ( targetFilenameNoFixup ),
                                installDirectory );
@@ -713,17 +941,22 @@ MingwBackend::GetInstallTargetFiles (
 
 void
 MingwBackend::OutputInstallTarget ( const string& sourceFilename,
-                                       const string& targetFilename,
-                                       const string& targetDirectory )
+                                   const string& targetFilename,
+                                   const string& targetDirectory )
 {
+       string fullTargetFilename;
+       if ( targetDirectory.length () > 0)
+               fullTargetFilename = targetDirectory + sSep + targetFilename;
+       else
+               fullTargetFilename = targetFilename;
        string normalizedTargetFilename = MingwModuleHandler::PassThruCacheDirectory (
-               NormalizeFilename ( targetDirectory + SSEP + targetFilename ),
+               NormalizeFilename ( fullTargetFilename ),
                installDirectory );
        string normalizedTargetDirectory = MingwModuleHandler::PassThruCacheDirectory (
                NormalizeFilename ( targetDirectory ),
                installDirectory );
        fprintf ( fMakefile,
-                 "%s: %s %s\n",
+                 "%s: %s %s\n",
                  normalizedTargetFilename.c_str (),
                  sourceFilename.c_str (),
                  normalizedTargetDirectory.c_str () );
@@ -742,9 +975,22 @@ MingwBackend::OutputNonModuleInstallTargets ()
        {
                const InstallFile& installfile = *ProjectNode.installfiles[i];
                OutputInstallTarget ( installfile.GetPath (),
-                                 installfile.newname,
-                                 installfile.base );
+                                     installfile.newname,
+                                     installfile.base );
+       }
+}
+
+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
@@ -753,14 +999,17 @@ 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 );
                }
        }
 }
@@ -768,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
@@ -780,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
@@ -794,7 +1043,7 @@ MingwBackend::OutputRegistryInstallTarget ()
 {
        string system32ConfigDirectory = NormalizeFilename (
                MingwModuleHandler::PassThruCacheDirectory (
-               "system32" SSEP "config" SSEP,
+               "system32" + sSep + "config" + sSep,
                installDirectory ) );
 
        string registrySourceFiles = GetRegistrySourceFiles ();
@@ -810,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" );
 }
@@ -822,13 +1072,51 @@ MingwBackend::GenerateInstallTarget ()
        vector<string> vInstallTargetFiles;
        GetInstallTargetFiles ( vInstallTargetFiles );
        string installTargetFiles = v2s ( vInstallTargetFiles, 5 );
+       string registryTargetFiles = GetRegistryTargetFiles ();
 
        fprintf ( fMakefile,
-                 "install: %s install_registry\n",
-                 installTargetFiles.c_str () );
+                 "install: %s %s\n",
+                 installTargetFiles.c_str (),
+                 registryTargetFiles.c_str () );
        OutputNonModuleInstallTargets ();
        OutputModuleInstallTargets ();
        OutputRegistryInstallTarget ();
        fprintf ( fMakefile,
                  "\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 ()
+{
+       intermediateDirectory->CreateRule ( fMakefile, "" );
+       outputDirectory->CreateRule ( fMakefile, "" );
+       installDirectory->CreateRule ( fMakefile, "" );
+}