* Implement <autoregister>
[reactos.git] / reactos / tools / rbuild / backend / mingw / mingw.cpp
index ea67903..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,172 +55,6 @@ 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:
@@ -246,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 ()
 {
@@ -261,6 +145,8 @@ MingwBackend::ProcessModules ()
                MingwModuleHandler* h = MingwModuleHandler::InstanciateHandler (
                        module,
                        this );
+               if ( use_pch && CanEnablePreCompiledHeaderSupportForModule ( module ) )
+                       h->EnablePreCompiledHeaderSupport ();
                if ( module.host == HostDefault )
                {
                        module.host = h->DefaultHost();
@@ -335,6 +221,7 @@ void
 MingwBackend::ProcessNormal ()
 {
        DetectCompiler ();
+       DetectBinutils ();
        DetectNetwideAssembler ();
        DetectPipeSupport ();
        DetectPCHSupport ();
@@ -349,6 +236,8 @@ MingwBackend::ProcessNormal ()
        GenerateDirectories ();
        UnpackWineResources ();
        GenerateTestSupportCode ();
+       GenerateCompilationUnitSupportCode ();
+       GenerateSysSetup ();
        GenerateProxyMakefiles ();
        CheckAutomaticDependencies ();
        CloseMakefile ();
@@ -362,7 +251,6 @@ MingwBackend::CreateMakefile ()
                throw AccessDeniedException ( ProjectNode.makefile );
        MingwModuleHandler::SetBackend ( this );
        MingwModuleHandler::SetMakefile ( fMakefile );
-       MingwModuleHandler::SetUsePch ( use_pch );
 }
 
 void
@@ -550,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;
 }
 
@@ -655,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
@@ -677,6 +567,27 @@ MingwBackend::GenerateTestSupportCode ()
        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
 {
@@ -711,7 +622,7 @@ MingwBackend::CheckAutomaticDependencies ()
 bool
 MingwBackend::IncludeDirectoryTarget ( const string& directory ) const
 {
-       if ( directory == "$(INTERMEDIATE)" SSEP "tools")
+       if ( directory == "$(INTERMEDIATE)" + sSep + "tools")
                return false;
        else
                return true;
@@ -733,7 +644,7 @@ MingwBackend::TryToDetectThisCompiler ( const string& compiler )
 {
        string command = ssprintf (
                "%s -v 1>%s 2>%s",
-               compiler.c_str (),
+               FixSeparatorForSystemCommand(compiler).c_str (),
                NUL,
                NUL );
        int exitcode = system ( command.c_str () );
@@ -778,13 +689,121 @@ MingwBackend::TryToDetectThisNetwideAssembler ( const string& assembler )
 {
        string command = ssprintf (
                "%s -h 1>%s 2>%s",
-               assembler.c_str (),
+               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 ()
 {
@@ -815,12 +834,12 @@ 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 1>%s 2>%s",
-               compilerCommand.c_str (),
+               FixSeparatorForSystemCommand(compilerCommand).c_str (),
                pipe_detection.c_str (),
                pipe_detectionObjectFilename.c_str (),
                NUL,
@@ -847,10 +866,10 @@ 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 1>%s 2>%s",
-               compilerCommand.c_str (),
+               FixSeparatorForSystemCommand(compilerCommand).c_str (),
                path.c_str (),
                NUL,
                NUL );
@@ -880,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 );
@@ -901,7 +920,7 @@ MingwBackend::GetModuleInstallTargetFiles (
                {
                        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 (
@@ -927,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 (
@@ -961,6 +980,19 @@ 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 ()
 {
@@ -971,8 +1003,9 @@ MingwBackend::OutputModuleInstallTargets ()
                        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,
@@ -984,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
@@ -996,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
@@ -1010,7 +1043,7 @@ MingwBackend::OutputRegistryInstallTarget ()
 {
        string system32ConfigDirectory = NormalizeFilename (
                MingwModuleHandler::PassThruCacheDirectory (
-               "system32" SSEP "config" SSEP,
+               "system32" + sSep + "config" + sSep,
                installDirectory ) );
 
        string registrySourceFiles = GetRegistrySourceFiles ();
@@ -1026,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" );
 }