From 64b336784507f7b8c9117cf5367a6abfc78a8ba3 Mon Sep 17 00:00:00 2001 From: Evan Wilde Date: Sat, 18 Nov 2023 15:51:13 -0800 Subject: cmGlobalGenerator: Allow passing language to GetLangaugeOutputExtension The original GetLanguageOutputExtension took a sourcefile instead of the name of the language itself. This implementation provided a convenient handler for when the SourceFile doesn't know what language it is, but there are times where we know the language, but don't necessarily have a source file. Adding an overload that takes the name of the language and returns the extension of that language, or empty string if no extension is registered. --- Source/cmGlobalGenerator.cxx | 32 +++++++++++++++++++------------- Source/cmGlobalGenerator.h | 2 ++ 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx index ca1afab..e74a8b0 100644 --- a/Source/cmGlobalGenerator.cxx +++ b/Source/cmGlobalGenerator.cxx @@ -1158,24 +1158,30 @@ std::string cmGlobalGenerator::GetLanguageOutputExtension( { const std::string& lang = source.GetLanguage(); if (!lang.empty()) { - auto const it = this->LanguageToOutputExtension.find(lang); - if (it != this->LanguageToOutputExtension.end()) { - return it->second; - } - } else { - // if no language is found then check to see if it is already an - // output extension for some language. In that case it should be ignored - // and in this map, so it will not be compiled but will just be used. - std::string const& ext = source.GetExtension(); - if (!ext.empty()) { - if (this->OutputExtensions.count(ext)) { - return ext; - } + return this->GetLanguageOutputExtension(lang); + } + // if no language is found then check to see if it is already an + // output extension for some language. In that case it should be ignored + // and in this map, so it will not be compiled but will just be used. + std::string const& ext = source.GetExtension(); + if (!ext.empty()) { + if (this->OutputExtensions.count(ext)) { + return ext; } } return ""; } +std::string cmGlobalGenerator::GetLanguageOutputExtension( + std::string const& lang) const +{ + auto const it = this->LanguageToOutputExtension.find(lang); + if (it != this->LanguageToOutputExtension.end()) { + return it->second; + } + return ""; +} + std::string cmGlobalGenerator::GetLanguageFromExtension(const char* ext) const { // if there is an extension and it starts with . then move past the diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h index aa54f69..d83b669 100644 --- a/Source/cmGlobalGenerator.h +++ b/Source/cmGlobalGenerator.h @@ -349,6 +349,8 @@ public: int GetLinkerPreference(const std::string& lang) const; //! What is the object file extension for a given source file? std::string GetLanguageOutputExtension(cmSourceFile const&) const; + //! What is the object file extension for a given language? + std::string GetLanguageOutputExtension(std::string const& lang) const; //! What is the configurations directory variable called? virtual const char* GetCMakeCFGIntDir() const { return "."; } -- cgit v0.12 From 9bed4f4d817f139f0c2e050d7420e1e247949fe4 Mon Sep 17 00:00:00 2001 From: Evan Wilde Date: Wed, 22 Nov 2023 11:09:35 -0800 Subject: Swift/Ninja: Split compilation model Splitting the Swift build into an object build and a separate link step, instead of building and linking in one step. The immediate benefit is LSP support because we are able to emit compile-commands for Swift files now. Additionally, it is possible to specify flags to the compile step, enabling folks to emit C and C++ headers from their Swift builds for C/C++ interop, without needing custom commands. Eventually, this gives us a path toward working object libraries. Object Libraries: - Object libraries don't work today because CMake doesn't emit targets for object libraries into the Ninja build file. - tl;dr: Object libraries work if they aren't WMO. Still need work to make WMO'd object libraries work. Object libraries still don't completely work with this patch because, while we emit the targets, the `TARGET_OBJECTS` generator expression expansion has a separate mechanism for determining what the names of the objects are based on the input source files, so targets that depend on an object library built with a whole-module optimization will depend on objects based on the name of the source file instead of the actual emitted object file. These features require being able to accurately model wholemodule builds though, because we actually need to track object files and WMO affects what objects are emitted. For that, we require CMP0157 use the NEW policy. When it's OLD, we have to fall back on the old behavior and cannot provide object libraries or the compile-commands for LSP. Issue: #25308 --- Modules/CMakeSwiftInformation.cmake | 72 +++++-- Source/cmLocalGenerator.cxx | 8 +- Source/cmLocalGenerator.h | 4 + Source/cmNinjaNormalTargetGenerator.cxx | 15 +- Source/cmNinjaTargetGenerator.cxx | 238 ++++++++++++++++++++- Source/cmNinjaTargetGenerator.h | 3 + .../Swift/IncrementalSwift-second-stdout.txt | 6 +- Tests/SwiftOnly/CMakeLists.txt | 24 ++- Tests/SwiftOnly/O.swift | 0 9 files changed, 323 insertions(+), 47 deletions(-) create mode 100644 Tests/SwiftOnly/O.swift diff --git a/Modules/CMakeSwiftInformation.cmake b/Modules/CMakeSwiftInformation.cmake index 9f4ed34..04d500e 100644 --- a/Modules/CMakeSwiftInformation.cmake +++ b/Modules/CMakeSwiftInformation.cmake @@ -116,36 +116,66 @@ endif() cmake_initialize_per_config_variable(CMAKE_Swift_FLAGS "Swift Compiler Flags") -# NOTE(compnerd) we do not have an object compile rule since we build the objects as part of the link step -if(NOT CMAKE_Swift_COMPILE_OBJECT) - set(CMAKE_Swift_COMPILE_OBJECT ":") -endif() - if(NOT CMAKE_Swift_NUM_THREADS MATCHES "^[0-9]+$") cmake_host_system_information(RESULT CMAKE_Swift_NUM_THREADS QUERY NUMBER_OF_LOGICAL_CORES) endif() -if(NOT CMAKE_Swift_CREATE_SHARED_LIBRARY) - set(CMAKE_Swift_CREATE_SHARED_LIBRARY " -j ${CMAKE_Swift_NUM_THREADS} -num-threads ${CMAKE_Swift_NUM_THREADS} -emit-library -o -module-name -module-link-name -emit-module -emit-module-path -emit-dependencies ${CMAKE_Swift_IMPLIB_LINKER_FLAGS} ") -endif() +# Swift split-compilation requires CMP0157 NEW policy +if(CMAKE_Swift_COMPILATION_MODE_DEFAULT) + set(CMAKE_Swift_PARALLEL_FLAGS "-j ${CMAKE_Swift_NUM_THREADS} -num-threads ${CMAKE_Swift_NUM_THREADS}") + if(NOT CMAKE_Swift_COMPILE_OBJECT) + # Omit the object output. The output is controlled by the output-file-map + # for normal builds. For wholemodule builds, CMake appends the appropriate + # flags. + set(CMAKE_Swift_COMPILE_OBJECT " ${CMAKE_Swift_PARALLEL_FLAGS} -c ") + endif() -if(NOT CMAKE_Swift_CREATE_SHARED_MODULE) - set(CMAKE_Swift_CREATE_SHARED_MODULE ${CMAKE_Swift_CREATE_SHARED_LIBRARY}) -endif() + if(NOT CMAKE_Swift_CREATE_SHARED_LIBRARY) + set(CMAKE_Swift_CREATE_SHARED_LIBRARY " ${CMAKE_Swift_PARALLEL_FLAGS} -emit-library -o ") + endif() -if(NOT CMAKE_Swift_LINK_EXECUTABLE) - set(CMAKE_Swift_LINK_EXECUTABLE " -j ${CMAKE_Swift_NUM_THREADS} -num-threads ${CMAKE_Swift_NUM_THREADS} -emit-executable -o -emit-dependencies ") -endif() + if(NOT CMAKE_Swift_CREATE_SHARED_MODULE) + set(CMAKE_Swift_CREATE_SHARED_MODULE ${CMAKE_Swift_CREATE_SHARED_LIBRARY}) + endif() -if(NOT CMAKE_Swift_LINK_EXECUTABLE_WITH_EXPORTS) - set(CMAKE_Swift_LINK_EXECUTABLE_WITH_EXPORTS "${CMAKE_Swift_LINK_EXECUTABLE} -emit-module -emit-module-path ${CMAKE_Swift_IMPLIB_LINKER_FLAGS}") -endif() + if(NOT CMAKE_Swift_LINK_EXECUTABLE) + set(CMAKE_Swift_LINK_EXECUTABLE " ${CMAKE_Swift_PARALLEL_FLAGS} -emit-executable -o ") + endif() -if(NOT CMAKE_Swift_CREATE_STATIC_LIBRARY) - set(CMAKE_Swift_CREATE_STATIC_LIBRARY " -j ${CMAKE_Swift_NUM_THREADS} -num-threads ${CMAKE_Swift_NUM_THREADS} -emit-library -static -o -module-name -module-link-name -emit-module -emit-module-path -emit-dependencies ") + if(NOT CMAKE_Swift_CREATE_STATIC_LIBRARY) + set(CMAKE_Swift_CREATE_STATIC_LIBRARY " -emit-library -static -o ") + set(CMAKE_Swift_ARCHIVE_CREATE " crs ") + set(CMAKE_Swift_ARCHIVE_FINISH "") + endif() + unset(CMAKE_Swift_PARALLEL_FLAGS) +else() + # NOTE(compnerd) we do not have an object compile rule since we build the objects as part of the link step + if(NOT CMAKE_Swift_COMPILE_OBJECT) + set(CMAKE_Swift_COMPILE_OBJECT ":") + endif() + + if(NOT CMAKE_Swift_CREATE_SHARED_LIBRARY) + set(CMAKE_Swift_CREATE_SHARED_LIBRARY " -j ${CMAKE_Swift_NUM_THREADS} -num-threads ${CMAKE_Swift_NUM_THREADS} -emit-library -o -module-name -module-link-name -emit-module -emit-module-path -emit-dependencies ${CMAKE_Swift_IMPLIB_LINKER_FLAGS} ") + endif() - set(CMAKE_Swift_ARCHIVE_CREATE " crs ") - set(CMAKE_Swift_ARCHIVE_FINISH "") + if(NOT CMAKE_Swift_CREATE_SHARED_MODULE) + set(CMAKE_Swift_CREATE_SHARED_MODULE ${CMAKE_Swift_CREATE_SHARED_LIBRARY}) + endif() + + if(NOT CMAKE_Swift_LINK_EXECUTABLE) + set(CMAKE_Swift_LINK_EXECUTABLE " -j ${CMAKE_Swift_NUM_THREADS} -num-threads ${CMAKE_Swift_NUM_THREADS} -emit-executable -o -emit-dependencies ") + endif() + + if(NOT CMAKE_Swift_LINK_EXECUTABLE_WITH_EXPORTS) + set(CMAKE_Swift_LINK_EXECUTABLE_WITH_EXPORTS "${CMAKE_Swift_LINK_EXECUTABLE} -emit-module -emit-module-path ${CMAKE_Swift_IMPLIB_LINKER_FLAGS}") + endif() + + if(NOT CMAKE_Swift_CREATE_STATIC_LIBRARY) + set(CMAKE_Swift_CREATE_STATIC_LIBRARY " -j ${CMAKE_Swift_NUM_THREADS} -num-threads ${CMAKE_Swift_NUM_THREADS} -emit-library -static -o -module-name -module-link-name -emit-module -emit-module-path -emit-dependencies ") + + set(CMAKE_Swift_ARCHIVE_CREATE " crs ") + set(CMAKE_Swift_ARCHIVE_FINISH "") + endif() endif() set(CMAKE_Swift_INFORMATION_LOADED 1) diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx index eaf38d4..e479931 100644 --- a/Source/cmLocalGenerator.cxx +++ b/Source/cmLocalGenerator.cxx @@ -1366,7 +1366,7 @@ std::vector> cmLocalGenerator::GetStaticLibraryFlags( { const std::string configUpper = cmSystemTools::UpperCase(config); std::vector> flags; - if (linkLanguage != "Swift") { + if (linkLanguage != "Swift" && !this->IsSplitSwiftBuild()) { std::string staticLibFlags; this->AppendFlags( staticLibFlags, @@ -3016,6 +3016,12 @@ cm::optional cmLocalGenerator::GetSwiftCompileMode( return cmSwiftCompileMode::Unknown; } +bool cmLocalGenerator::IsSplitSwiftBuild() const +{ + return cmNonempty(this->GetMakefile()->GetDefinition( + "CMAKE_Swift_COMPILATION_MODE_DEFAULT")); +} + namespace { inline void RegisterUnitySources(cmGeneratorTarget* target, cmSourceFile* sf, diff --git a/Source/cmLocalGenerator.h b/Source/cmLocalGenerator.h index af5b35e..3ec349b 100644 --- a/Source/cmLocalGenerator.h +++ b/Source/cmLocalGenerator.h @@ -562,6 +562,10 @@ public: cm::optional GetSwiftCompileMode( cmGeneratorTarget const* target, std::string const& config); + // Can we build Swift with a separate object build and link step + // (If CMP0157 is NEW, we can do a split build) + bool IsSplitSwiftBuild() const; + protected: // The default implementation converts to a Windows shortpath to // help older toolchains handle spaces and such. A generator may diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx index 5e7bb6e..99ea009 100644 --- a/Source/cmNinjaNormalTargetGenerator.cxx +++ b/Source/cmNinjaNormalTargetGenerator.cxx @@ -1209,7 +1209,10 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement( globalGen->GetByproductsForCleanTarget(config).push_back(targetOutputReal); } - if (this->TargetLinkLanguage(config) == "Swift") { + // If we can't split the Swift build model (CMP0157 is OLD or unset), fall + // back on the old one-step "build/link" logic. + if (!this->GetLocalGenerator()->IsSplitSwiftBuild() && + this->TargetLinkLanguage(config) == "Swift") { vars["SWIFT_LIBRARY_NAME"] = [this, config]() -> std::string { cmGeneratorTarget::Names targetNames = this->GetGeneratorTarget()->GetLibraryNames(config); @@ -1222,12 +1225,12 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement( cmOutputConverter::SHELL); vars["SWIFT_SOURCES"] = [this, config]() -> std::string { - std::vector sources; + std::vector sourceFiles; std::stringstream oss; - this->GetGeneratorTarget()->GetObjectSources(sources, config); + this->GetGeneratorTarget()->GetObjectSources(sourceFiles, config); cmLocalGenerator const* LocalGen = this->GetLocalGenerator(); - for (const auto& source : sources) { + for (const auto& source : sourceFiles) { const std::string sourcePath = source->GetLanguage() == "Swift" ? this->GetCompiledSourceNinjaPath(source) : this->GetObjectFilePath(source, config); @@ -1245,10 +1248,8 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement( vars["FLAGS"] = this->GetFlags("Swift", config); vars["INCLUDES"] = this->GetIncludes("Swift", config); this->GenerateSwiftOutputFileMap(config, vars["FLAGS"]); - } - // Compute specific libraries to link with. - if (this->TargetLinkLanguage(config) == "Swift") { + // Compute specific libraries to link with. std::vector sources; gt->GetObjectSources(sources, config); for (const auto& source : sources) { diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx index 666d7b6..6367075 100644 --- a/Source/cmNinjaTargetGenerator.cxx +++ b/Source/cmNinjaTargetGenerator.cxx @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -31,6 +32,7 @@ #include "cmGlobalCommonGenerator.h" #include "cmGlobalNinjaGenerator.h" #include "cmList.h" +#include "cmListFileCache.h" #include "cmLocalGenerator.h" #include "cmLocalNinjaGenerator.h" #include "cmMakefile.h" @@ -47,6 +49,7 @@ #include "cmStringAlgorithms.h" #include "cmSystemTools.h" #include "cmTarget.h" +#include "cmTargetDepend.h" #include "cmValue.h" #include "cmake.h" @@ -145,7 +148,7 @@ std::string cmNinjaTargetGenerator::LanguageScanRule( bool cmNinjaTargetGenerator::NeedExplicitPreprocessing( std::string const& lang) const { - return lang == "Fortran"; + return lang == "Fortran" || lang == "Swift"; } bool cmNinjaTargetGenerator::CompileWithDefines(std::string const& lang) const @@ -1136,9 +1139,18 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatements( std::vector objectSources; this->GeneratorTarget->GetObjectSources(objectSources, config); + std::vector swiftSources; + for (cmSourceFile const* sf : objectSources) { - this->WriteObjectBuildStatement(sf, config, fileConfig, firstForConfig); + if (sf->GetLanguage() == "Swift") { + swiftSources.push_back(sf); + } else { + this->WriteObjectBuildStatement(sf, config, fileConfig, + firstForConfig); + } } + WriteSwiftObjectBuildStatement(swiftSources, config, fileConfig, + firstForConfig); } { @@ -1234,9 +1246,11 @@ void cmNinjaTargetGenerator::GenerateSwiftOutputFileMap( if (cmValue name = target->GetProperty("Swift_DEPENDENCIES_FILE")) { return *name; } - return this->ConvertToNinjaPath(cmStrCat(target->GetSupportDirectory(), - '/', config, '/', - target->GetName(), ".swiftdeps")); + return this->GetLocalGenerator()->ConvertToOutputFormat( + this->ConvertToNinjaPath(cmStrCat(target->GetSupportDirectory(), '/', + config, '/', target->GetName(), + ".swiftdeps")), + cmOutputConverter::SHELL); }(); std::string mapFilePath = @@ -1254,8 +1268,10 @@ void cmNinjaTargetGenerator::GenerateSwiftOutputFileMap( // Add flag this->LocalGenerator->AppendFlags(flags, "-output-file-map"); - this->LocalGenerator->AppendFlagEscape(flags, - ConvertToNinjaPath(mapFilePath)); + this->LocalGenerator->AppendFlagEscape( + flags, + this->GetLocalGenerator()->ConvertToOutputFormat( + ConvertToNinjaPath(mapFilePath), cmOutputConverter::SHELL)); } namespace { @@ -1862,6 +1878,214 @@ void cmNinjaTargetGenerator::WriteCxxModuleBmiBuildStatement( bmiBuild, commandLineLengthLimit); } +void cmNinjaTargetGenerator::WriteSwiftObjectBuildStatement( + std::vector const& sources, std::string const& config, + std::string const& fileConfig, bool firstForConfig) +{ + // Swift sources are compiled as a module, not individually like with C/C++. + // Flags, header search paths, and definitions are passed to the entire + // module build, but we still need to emit compile-commands for each source + // file in order to support CMAKE_EXPORT_COMPILE_COMMANDS. + // In whole-module mode, with a single thread, the Swift compiler will + // only emit a single object file, but if more than one thread is specified, + // or building in other modes, the compiler will emit multiple object files. + // When building a single-output, we do not provide an output-file-map (OFM), + // and instead pass `-o` to tell the compiler where to write the object. + // When building multiple outputs, we provide an OFM to tell the compiler + // where to put each object. + // + // + // Per-Target (module): + // - Flags + // - Definitions + // - Include paths + // - (single-output) output object filename + // - Swiftmodule + // + // Per-File: + // - compile-command + // - (multi-output) OFM data + // - (multi-output) output object filename + // + // Note: Due to the differences in the build models, we are only able to + // build the object build-graph if we know what mode the target is built in. + // For that, we need the "NEW" behavior for CMP0157. Otherwise, we have to + // fall back on the old "linker" build. Otherwise, this should be + // indistinguishable from the old behavior. + // + // FIXME(#25490): Add response file support to Swift object build step + // FIXME(#25491): Include all files in module in compile_commands.json + + if (sources.empty()) { + return; + } + + cmSwiftCompileMode compileMode; + if (cm::optional optionalCompileMode = + this->LocalGenerator->GetSwiftCompileMode(this->GeneratorTarget, + config)) { + compileMode = *optionalCompileMode; + } else { + // CMP0157 is not NEW, bailing early! + return; + } + + auto getTargetPropertyOrDefault = + [](cmGeneratorTarget const& target, std::string const& property, + std::string defaultValue) -> std::string { + if (cmValue value = target.GetProperty(property)) { + return *value; + } + return defaultValue; + }; + + std::string const language = "Swift"; + std::string const objectDir = this->ConvertToNinjaPath( + cmStrCat(this->GeneratorTarget->GetSupportDirectory(), + this->GetGlobalGenerator()->ConfigDirectory(config))); + + cmGeneratorTarget const& target = *this->GeneratorTarget; + cmNinjaBuild objBuild( + this->LanguageCompilerRule(language, config, WithScanning::No)); + cmNinjaVars& vars = objBuild.Variables; + + std::string const moduleName = + getTargetPropertyOrDefault(target, "Swift_MODULE_NAME", target.GetName()); + std::string const moduleDirectory = getTargetPropertyOrDefault( + target, "Swift_MODULE_DIRECTORY", + target.LocalGenerator->GetCurrentBinaryDirectory()); + std::string const moduleFilename = getTargetPropertyOrDefault( + target, "Swift_MODULE", cmStrCat(moduleName, ".swiftmodule")); + std::string const moduleFilepath = + this->ConvertToNinjaPath(cmStrCat(moduleDirectory, '/', moduleFilename)); + + bool const isSingleOutput = [this, compileMode]() -> bool { + bool isMultiThread = false; + if (cmValue numThreadStr = + this->GetMakefile()->GetDefinition("CMAKE_Swift_NUM_THREADS")) { + unsigned long numThreads; + cmStrToULong(*numThreadStr, &numThreads); + // numThreads == 1 is multi-threaded according to swiftc + isMultiThread = numThreads > 0; + } + return !isMultiThread && compileMode == cmSwiftCompileMode::Wholemodule; + }(); + + // Swift modules only make sense to emit from things that can be imported. + // Executables that don't export symbols can't be imported, so don't try to + // emit a swiftmodule for them. It will break. + if (target.GetType() != cmStateEnums::EXECUTABLE || + target.IsExecutableWithExports()) { + std::string const emitModuleFlag = "-emit-module"; + std::string const modulePathFlag = "-emit-module-path"; + this->LocalGenerator->AppendFlags( + vars["FLAGS"], { emitModuleFlag, modulePathFlag, moduleFilepath }); + objBuild.Outputs.push_back(moduleFilepath); + + std::string const moduleNameFlag = "-module-name"; + this->LocalGenerator->AppendFlags( + vars["FLAGS"], cmStrCat(moduleNameFlag, ' ', moduleName)); + } + + if (target.GetType() != cmStateEnums::EXECUTABLE) { + std::string const libraryLinkNameFlag = "-module-link-name"; + std::string const libraryLinkName = + this->GetGeneratorTarget()->GetLibraryNames(config).Base; + this->LocalGenerator->AppendFlags( + vars["FLAGS"], cmStrCat(libraryLinkNameFlag, ' ', libraryLinkName)); + } + + // Without `-emit-library` or `-emit-executable`, targets with a single + // source file parse as a Swift script instead of like normal source. For + // non-executable targets, append this to ensure that they are parsed like a + // normal source. + if (target.GetType() != cmStateEnums::EXECUTABLE) { + this->LocalGenerator->AppendFlags(vars["FLAGS"], "-parse-as-library"); + } + + this->LocalGenerator->AppendFlags(vars["FLAGS"], + this->GetFlags(language, config)); + vars["DEFINES"] = this->GetDefines(language, config); + vars["INCLUDES"] = this->GetIncludes(language, config); + + // target-level object filename + std::string const targetObjectFilename = this->ConvertToNinjaPath(cmStrCat( + objectDir, '/', moduleName, + this->GetGlobalGenerator()->GetLanguageOutputExtension(language))); + + if (isSingleOutput) { + this->LocalGenerator->AppendFlags(vars["FLAGS"], + cmStrCat("-o ", targetObjectFilename)); + objBuild.Outputs.push_back(targetObjectFilename); + this->Configs[config].Objects.push_back(targetObjectFilename); + } + + for (cmSourceFile const* sf : sources) { + // Add dependency to object build on each source file + std::string const sourceFilePath = this->GetCompiledSourceNinjaPath(sf); + objBuild.ExplicitDeps.push_back(sourceFilePath); + + if (isSingleOutput) { + if (firstForConfig) { + this->ExportObjectCompileCommand( + language, sourceFilePath, objectDir, targetObjectFilename, + cmSystemTools::GetFilenamePath(targetObjectFilename), vars["FLAGS"], + vars["DEFINES"], vars["INCLUDES"], + /*compile pdb*/ "", /*target pdb*/ "", config); + } + } else { + // Object outputs + std::string const objectFilepath = + this->ConvertToNinjaPath(this->GetObjectFilePath(sf, config)); + this->EnsureParentDirectoryExists(objectFilepath); + objBuild.Outputs.push_back(objectFilepath); + this->Configs[config].Objects.push_back(objectFilepath); + + // Add OFM data + this->EmitSwiftDependencyInfo(sf, config); + + // Emit compile commands + if (firstForConfig) { + this->ExportObjectCompileCommand( + language, sourceFilePath, objectDir, objectFilepath, + cmSystemTools::GetFilenamePath(objectFilepath), vars["FLAGS"], + vars["DEFINES"], vars["INCLUDES"], + /*compile pdb*/ "", + /*target pdb*/ "", config); + } + } + } + + if (!isSingleOutput) { + this->GenerateSwiftOutputFileMap(config, vars["FLAGS"]); + } + + for (cmTargetDepend const& dep : + this->GetGlobalGenerator()->GetTargetDirectDepends(&target)) { + if (!dep->IsLanguageUsed("Swift", config)) { + continue; + } + // Add dependencies on the emitted swiftmodule file from swift targets we + // depend on + std::string const depModuleName = + getTargetPropertyOrDefault(*dep, "Swift_MODULE_NAME", dep->GetName()); + std::string const depModuleDir = getTargetPropertyOrDefault( + *dep, "Swift_MODULE_DIRECTORY", + dep->LocalGenerator->GetCurrentBinaryDirectory()); + std::string const depModuleFilename = getTargetPropertyOrDefault( + *dep, "Swift_MODULE", cmStrCat(depModuleName, ".swiftmodule")); + std::string const depModuleFilepath = + this->ConvertToNinjaPath(cmStrCat(depModuleDir, '/', depModuleFilename)); + objBuild.ImplicitDeps.push_back(depModuleFilepath); + } + + objBuild.OrderOnlyDeps.push_back(this->OrderDependsTargetForTarget(config)); + + // Write object build + this->GetGlobalGenerator()->WriteBuild(this->GetImplFileStream(fileConfig), + objBuild); +} + void cmNinjaTargetGenerator::WriteTargetDependInfo(std::string const& lang, const std::string& config) { diff --git a/Source/cmNinjaTargetGenerator.h b/Source/cmNinjaTargetGenerator.h index 2131f6d..7a68599 100644 --- a/Source/cmNinjaTargetGenerator.h +++ b/Source/cmNinjaTargetGenerator.h @@ -171,6 +171,9 @@ protected: const std::string& config, const std::string& fileConfig, bool firstForConfig); + void WriteSwiftObjectBuildStatement( + std::vector const& sources, const std::string& config, + const std::string& fileConfig, bool firstForConfig); void WriteObjectBuildStatement(cmSourceFile const* source, const std::string& config, const std::string& fileConfig, diff --git a/Tests/RunCMake/Swift/IncrementalSwift-second-stdout.txt b/Tests/RunCMake/Swift/IncrementalSwift-second-stdout.txt index bb08a49..d644d6b 100644 --- a/Tests/RunCMake/Swift/IncrementalSwift-second-stdout.txt +++ b/Tests/RunCMake/Swift/IncrementalSwift-second-stdout.txt @@ -1,3 +1,3 @@ -.*Linking Swift static library libA.a -.*Linking Swift static library libB.a -FAILED: libB.a CMakeFiles/B.dir/b.swift.o B.swiftmodule +.*Building Swift object A.swiftmodule CMakeFiles/A.dir/a.swift.o +.*Building Swift object B.swiftmodule CMakeFiles/B.dir/b.swift.o +FAILED: B.swiftmodule CMakeFiles/B.dir/b.swift.o diff --git a/Tests/SwiftOnly/CMakeLists.txt b/Tests/SwiftOnly/CMakeLists.txt index 7de1e04..2aa5710 100644 --- a/Tests/SwiftOnly/CMakeLists.txt +++ b/Tests/SwiftOnly/CMakeLists.txt @@ -43,6 +43,13 @@ add_library(N N.swift) target_link_libraries(N PUBLIC M) +if(NOT XCODE_VERSION OR XCODE_VERSION VERSION_GREATER_EQUAL 9.0) + # TODO: Add a wholemodule object-library test once that is working + add_library(O OBJECT O.swift L.swift) + target_link_libraries(N PUBLIC O) + set_target_properties(O PROPERTIES Swift_COMPILATION_MODE "incremental") +endif() + # Dummy to make sure generation works with such targets. add_library(SwiftIface INTERFACE) target_link_libraries(SwiftOnly PRIVATE SwiftIface) @@ -59,14 +66,15 @@ if(CMAKE_Swift_COMPILER_VERSION VERSION_GREATER_EQUAL 5.2) endif() function(test_cmp0157_default mode) - - cmake_policy(GET CMP0157 cmp0157_wmo) - if(cmp0157_wmo STREQUAL "NEW") - set(CMAKE_Swift_COMPILATION_MODE "${mode}") - add_executable(hi_${mode} main.swift) - get_target_property(${mode}_swift_comp_mode hi_${mode} "Swift_COMPILATION_MODE") - if(NOT ${mode}_swift_comp_mode STREQUAL ${mode}) - message(SEND_ERROR "expected ${mode} -- found ${${mode}_swift_comp_mode}") + if(POLICY CMP0157) + cmake_policy(GET CMP0157 cmp0157_wmo) + if(cmp0157_wmo STREQUAL "NEW") + set(CMAKE_Swift_COMPILATION_MODE "${mode}") + add_executable(hi_${mode} main.swift) + get_target_property(${mode}_swift_comp_mode hi_${mode} "Swift_COMPILATION_MODE") + if(NOT ${mode}_swift_comp_mode STREQUAL ${mode}) + message(SEND_ERROR "expected ${mode} -- found ${${mode}_swift_comp_mode}") + endif() endif() endif() endfunction() diff --git a/Tests/SwiftOnly/O.swift b/Tests/SwiftOnly/O.swift new file mode 100644 index 0000000..e69de29 -- cgit v0.12