CORE::CString GenerateContentForAndroidMakefile( const TModuleInfoEntryPairVector& mergeLinks , const TModuleInfo& moduleInfo , const CORE::CString& moduleRoot , bool addGeneratorCompileTimeToOutput , TStringSet& ndkModulesUsed ) {GUCEF_TRACE; CORE::CString contentPrefix = makefileHeader; if ( addGeneratorCompileTimeToOutput ) { contentPrefix += "#" "# The project generator version used was compiled on " __TIME__ __DATE__ "\n" "#\n\n"; } contentPrefix += "ifndef MY_MODULE_PATH\n" " MY_MODULE_PATH := $(call my-dir)\n" "endif\n" "LOCAL_PATH := $(MY_MODULE_PATH)\n\n" "include $(CLEAR_VARS)\n\n" "@echo Module path: $(MY_MODULE_PATH)\n" "LOCAL_MODULE := " + moduleInfo.name + "\n"; if ( ( MODULETYPE_SHARED_LIBRARY == moduleInfo.moduleType ) || ( MODULETYPE_STATIC_LIBRARY == moduleInfo.moduleType ) ) { contentPrefix += "LOCAL_MODULE_FILENAME := lib" + moduleInfo.name + "\n"; } contentPrefix += "@echo Module name: $(LOCAL_MODULE)\n\n"; // Generate the source files section CORE::CString srcFilesSection = "LOCAL_SRC_FILES := \\\n"; bool firstLoop = true; TStringVectorMap::const_iterator i = moduleInfo.sourceDirs.begin(); while ( i != moduleInfo.sourceDirs.end() ) { const CORE::CString& srcDir = (*i).first; const TStringVector& srcFiles = (*i).second; TStringVector::const_iterator n = srcFiles.begin(); while ( n != srcFiles.end() ) { if ( !firstLoop ) { srcFilesSection += " \\\n"; } firstLoop = false; CORE::CString relFilePath = srcDir; CORE::AppendToPath( relFilePath, (*n) ); srcFilesSection += " " + relFilePath.ReplaceChar( '\\', '/' ); ++n; } ++i; } // Add some spacing for readability srcFilesSection += "\n\n"; // Generate the included files section // for android make files we only need the path // it will locate the header file on its own CORE::CString includeFilesSection = "LOCAL_C_INCLUDES := \\\n"; i = moduleInfo.includeDirs.begin(); firstLoop = true; while ( i != moduleInfo.includeDirs.end() ) { if ( !firstLoop ) { includeFilesSection += " \\\n"; } firstLoop = false; const CORE::CString& dir = (*i).first; if ( !dir.IsNULLOrEmpty() ) { includeFilesSection += " $(MY_MODULE_PATH)/" + dir.ReplaceChar( '\\', '/' ); } else { // Support the use-case where the include dir is empty because the moduleinfo dir == include dir includeFilesSection += " $(MY_MODULE_PATH)"; } ++i; } // We also need to add the include paths required to find headers // refered to because of dependencies TStringSet::const_iterator n = moduleInfo.dependencyIncludeDirs.begin(); while ( n != moduleInfo.dependencyIncludeDirs.end() ) { if ( !firstLoop ) { includeFilesSection += " \\\n"; } firstLoop = false; includeFilesSection += " $(MY_MODULE_PATH)/" + (*n).ReplaceChar( '\\', '/' ); ++n; } // Add some spacing for readability includeFilesSection += "\n\n"; // Now we add the preprocessor definitions CORE::CString preprocessorSection; if ( !moduleInfo.preprocessorSettings.defines.empty() ) { preprocessorSection = "LOCAL_CFLAGS :="; TStringSet::const_iterator m = moduleInfo.preprocessorSettings.defines.begin(); while ( m != moduleInfo.preprocessorSettings.defines.end() ) { preprocessorSection += " -D" + (*m); ++m; } // Add some spacing for readability preprocessorSection += "\n\n"; } // Now we add the compiler flags, if any // For Android we only support the GCC compilers CORE::CString compilerSection; TStringMap::const_iterator p = moduleInfo.compilerSettings.compilerFlags.find( "GCC" ); if ( p != moduleInfo.compilerSettings.compilerFlags.end() ) { compilerSection = "LOCAL_CFLAGS +=" + (*p).second + "\n\n"; } p = moduleInfo.compilerSettings.compilerFlags.find( "G++" ); if ( p != moduleInfo.compilerSettings.compilerFlags.end() ) { compilerSection = "LOCAL_CPPFLAGS +=" + (*p).second + "\n\n"; } // Now we will add all the dependency linking instructions. // For some reason it matters, at specification time, to Android's build // system whether the module you are linking to is a dynamically linked module // or a statically linked module. As such we have to figure out which is which. // // We make an alphabetical list instead of creating the section right away because // we dont want the order to vary in the makefile because such changes cause the NDK // to build the code again for no reason. CORE::CString linkingErrorSection; TStringSet linkedSharedLibraries; TStringSet linkedStaticLibraries; TStringSet linkedRuntimeLibraries; TModuleTypeMap::const_iterator m = moduleInfo.linkerSettings.linkedLibraries.begin(); while ( m != moduleInfo.linkerSettings.linkedLibraries.end() ) { const CORE::CString& linkedLibName = (*m).first; TModuleType linkedLibType = (*m).second; switch ( linkedLibType ) { case MODULETYPE_EXECUTABLE: { // This is really nasty but the best option for now... // It is possible to link to exported symbols from an executable // under linux and as such we will leverage this here linkedSharedLibraries.insert( linkedLibName ); break; } case MODULETYPE_SHARED_LIBRARY: { linkedSharedLibraries.insert( linkedLibName ); break; } case MODULETYPE_STATIC_LIBRARY: { linkedStaticLibraries.insert( linkedLibName ); break; } case MODULETYPE_CODE_INTEGRATE_LOCATION: case MODULETYPE_HEADER_INTEGRATE_LOCATION: case MODULETYPE_HEADER_INCLUDE_LOCATION: { // Skip this, no linking required break; } default: { // Since the depedendency module type was not predefined we will investigate among // the other modules to try to determine the nature of the linked module const TModuleInfo* linkedDependency = FindModuleByName( mergeLinks, linkedLibName ); if ( NULL != linkedDependency ) { // The module we are linking too is part of this project. // As such we can simply check the other module's info // to find out wheter its a dynamically linked module or not // which in turn tells us how to instruct the Android build system // to link. switch( linkedDependency->moduleType ) { case MODULETYPE_SHARED_LIBRARY: { linkedSharedLibraries.insert( linkedLibName ); break; } case MODULETYPE_STATIC_LIBRARY: { linkedStaticLibraries.insert( linkedLibName ); break; } case MODULETYPE_EXECUTABLE: { // This is really nasty but the best option for now... // It is possible to link to exported symbols from an executable // under linux and as such we will leverage this here linkedSharedLibraries.insert( linkedLibName ); break; } case MODULETYPE_HEADER_INTEGRATE_LOCATION: case MODULETYPE_CODE_INTEGRATE_LOCATION: { // Dont do anything. // The files for this 'module' have already been merged into the dependent module break; } case MODULETYPE_HEADER_INCLUDE_LOCATION: { // Don't have to do anything. // Due to the auto-dependency tracking of include paths the header paths will have been added to // whatever module depends on this 'module' break; } default: { linkingErrorSection += "# *** ERROR *** Finish me\n" "# Unable to determing module type from the source information\n" "# Please edit the line below to manually set the correct linking method for this dependency\n"; linkingErrorSection += "#LOCAL_<(LDLIBS???)> += " + moduleInfo.name + "\n\n"; GUCEF_ERROR_LOG( CORE::LOGLEVEL_NORMAL, "Error: the module " + moduleInfo.name + " does not have a useable module type set, you will have to manually edit the file to correct the error" ); break; } } } else { // If we get here then this dependency is not on a module which is part of the project // As such we cannot build this module thus the only approriote linking method would seem // to be the one where we simply instruct the linker to load this dependency at runtime. // This will typically be the case for any Android NDK modules we have to link to. linkedRuntimeLibraries.insert( linkedLibName ); } } } ++m; } CORE::CString linkingSection; CORE::CString linkedSharedLibrariesSection = "\nLOCAL_SHARED_LIBRARIES := \\\n"; CORE::CString linkedStaticLibrariesSection = "\nLOCAL_STATIC_LIBRARIES := \\\n"; CORE::CString linkedRuntimeLibrariesSection = "\nLOCAL_LDLIBS := \\\n"; // Based on what was found we will construct the linking section bool first = true; n = linkedSharedLibraries.begin(); while ( n != linkedSharedLibraries.end() ) { if ( !first ) { linkedSharedLibrariesSection += " \\\n"; } linkedSharedLibrariesSection += " " + (*n); first = false; ++n; } first = true; n = linkedStaticLibraries.begin(); while ( n != linkedStaticLibraries.end() ) { if ( !first ) { linkedStaticLibrariesSection += " \\\n"; } linkedStaticLibrariesSection += " " + (*n); first = false; ++n; } first = true; n = linkedRuntimeLibraries.begin(); while ( n != linkedRuntimeLibraries.end() ) { if ( !first ) { linkedRuntimeLibrariesSection += " \\\n"; } linkedRuntimeLibrariesSection += " -l" + (*n); first = false; ++n; } if ( !linkedSharedLibraries.empty() ) { linkingSection += linkedSharedLibrariesSection + "\n\n"; } if ( !linkedStaticLibraries.empty() ) { linkingSection += linkedStaticLibrariesSection + "\n\n"; } if ( !linkedRuntimeLibraries.empty() ) { linkingSection += linkedRuntimeLibrariesSection + "\n\n"; } linkingSection += linkingErrorSection; // Check if we have a file on disk of information which is to be inserted into // our automatically generated make file CORE::CString manualContent; CORE::CString manualContentFilePath = moduleRoot; CORE::AppendToPath( manualContentFilePath, "AndroidAddition.mk" ); if ( CORE::FileExists( manualContentFilePath ) ) { if ( CORE::LoadTextFileAsString( manualContentFilePath , manualContent ) ) { GUCEF_LOG( CORE::LOGLEVEL_NORMAL, "Successfully loaded manually defined content for module " + moduleInfo.name + " from addition file " + manualContentFilePath ); } else { manualContent = "# *** ERROR *** Finish me\n" "# Unable to load manually defined content from detected AndroidAddition.mk file\n" "# Please manually insert its contents here\n\n"; GUCEF_ERROR_LOG( CORE::LOGLEVEL_NORMAL, "Error: the module " + moduleInfo.name + " has manually defined content in a AndroidAddition.mk file but it could not be loaded, you will have to manually edit the file to correct the error" ); } } // Now generate the latter part of the file which contains more meta data about the module CORE::CString contentSuffix; switch ( moduleInfo.moduleType ) { case MODULETYPE_SHARED_LIBRARY: { contentSuffix += "include $(BUILD_SHARED_LIBRARY)\n\n"; break; } case MODULETYPE_STATIC_LIBRARY: { contentSuffix += "include $(BUILD_STATIC_LIBRARY)\n\n"; break; } case MODULETYPE_EXECUTABLE: { contentSuffix += "include $(BUILD_EXECUTABLE)\n\n"; break; } default: { contentSuffix += "# *** ERROR *** Finish me\n" "# Unable to determine module type from the source information\n" "# Please edit the line below to manually set the correct module type to build\n" "#include $(BUILD_???)\n\n"; GUCEF_ERROR_LOG( CORE::LOGLEVEL_NORMAL, "Error: the module " + moduleInfo.name + " does not have a useable module type set, you will have to manually edit the file to correct the error" ); break; } } // Check for NDK static libraries to import if ( !linkedStaticLibraries.empty() ) { TStringSet::iterator a = linkedStaticLibraries.find( "android_native_app_glue" ); if ( a != linkedStaticLibraries.end() ) { ndkModulesUsed.insert( "android_native_app_glue" ); contentSuffix += "$(call import-module,android/native_app_glue)\n"; } a = linkedStaticLibraries.find( "cpufeatures" ); if ( a != linkedStaticLibraries.end() ) { ndkModulesUsed.insert( "cpufeatures" ); contentSuffix += "$(call import-module,android/cpufeatures)\n"; } } return contentPrefix + srcFilesSection + includeFilesSection + preprocessorSection + compilerSection + linkingSection + manualContent + contentSuffix; }
CORE::CString GenerateContentForAndroidProjectMakefile( const CORE::CString& projectName , const TModuleInfoEntryPairVector& mergeLinks , const CORE::CString& outputDir , bool addGeneratorCompileTimeToOutput , const TStringSet& ndkModulesUsed ) {GUCEF_TRACE; GUCEF_LOG( CORE::LOGLEVEL_NORMAL, "Generating Android makefile content for overall project file regarding project \"" + projectName + "\"" ); CORE::CString contentPrefix = makefileHeader; contentPrefix += "#\n" "# This is the project makefile which includes all modules which are part of this project\n" "#\n"; contentPrefix += "# PROJECT: \"" + projectName + "\"\n#\n\n"; if ( addGeneratorCompileTimeToOutput ) { contentPrefix += "#" "# The project generator version used was compiled on " __TIME__ __DATE__ "\n" "#\n\n"; } contentPrefix += "ifndef PROJECT_ROOT_PATH\n" " PROJECT_ROOT_PATH := $(call my-dir)\n" "endif\n\n" "include $(CLEAR_VARS)\n\n"; // Include makefiles for NDK modules used CORE::CString moduleListSection; /* -> Not needed if ( !ndkModulesUsed.empty() ) { TStringSet::iterator i = ndkModulesUsed.find( "android_native_app_glue" ); if ( i != ndkModulesUsed.end() ) { moduleListSection += "MY_MODULE_PATH := $(ANDROIDNDK)/sources/android/native_app_glue\n"; moduleListSection += "include $(MY_MODULE_PATH)/Android.mk\n\n"; } i = ndkModulesUsed.find( "cpufeatures" ); if ( i != ndkModulesUsed.end() ) { moduleListSection += "MY_MODULE_PATH := $(ANDROIDNDK)/sources/android/cpufeatures\n"; moduleListSection += "include $(MY_MODULE_PATH)/Android.mk\n\n"; } } */ // Include each module's makefile in the order listed as their build order const TModuleInfo* currentModule = FindFirstModuleAccordingToBuildOrder( mergeLinks ); while ( NULL != currentModule ) { if ( ( MODULETYPE_HEADER_INCLUDE_LOCATION != currentModule->moduleType ) && ( MODULETYPE_HEADER_INTEGRATE_LOCATION != currentModule->moduleType ) && ( MODULETYPE_CODE_INTEGRATE_LOCATION != currentModule->moduleType ) ) { // Get relative path from the outputDir to the other module const TModuleInfoEntry* fullModuleInfo = FindModuleInfoEntryForMergedInfo( mergeLinks, *currentModule ); CORE::CString relativePathToModule = CORE::GetRelativePathToOtherPathRoot( outputDir, fullModuleInfo->rootDir ); relativePathToModule = relativePathToModule.ReplaceChar( '\\', '/' ); // Add entry for this module to the project file moduleListSection += "MY_MODULE_PATH := $(PROJECT_ROOT_PATH)/" + relativePathToModule + "\n"; moduleListSection += "include $(MY_MODULE_PATH)/Android.mk\n\n"; } // Done with this module, go to the next one currentModule = FindNextModuleAccordingToBuildOrder( mergeLinks, *currentModule ); } return contentPrefix + moduleListSection; }