/src/CMake/Source/cmFastbuildNormalTargetGenerator.cxx
Line | Count | Source |
1 | | /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
2 | | file LICENSE.rst or https://cmake.org/licensing for details. */ |
3 | | |
4 | | #include "cmFastbuildNormalTargetGenerator.h" |
5 | | |
6 | | #include <algorithm> |
7 | | #include <array> |
8 | | #include <cstddef> |
9 | | #include <functional> |
10 | | #include <iterator> |
11 | | #include <map> |
12 | | #include <sstream> |
13 | | #include <string> |
14 | | #include <unordered_map> |
15 | | #include <unordered_set> |
16 | | #include <utility> |
17 | | |
18 | | #include <cm/memory> |
19 | | #include <cm/optional> |
20 | | #include <cm/string_view> |
21 | | #include <cmext/string_view> |
22 | | |
23 | | #include "cmsys/FStream.hxx" |
24 | | |
25 | | #include "cmCommonTargetGenerator.h" |
26 | | #include "cmCryptoHash.h" |
27 | | #include "cmFastbuildTargetGenerator.h" |
28 | | #include "cmGeneratedFileStream.h" |
29 | | #include "cmGeneratorExpression.h" |
30 | | #include "cmGeneratorFileSet.h" |
31 | | #include "cmGeneratorFileSets.h" |
32 | | #include "cmGeneratorTarget.h" |
33 | | #include "cmGlobalCommonGenerator.h" |
34 | | #include "cmGlobalFastbuildGenerator.h" |
35 | | #include "cmLinkLineComputer.h" |
36 | | #include "cmLinkLineDeviceComputer.h" |
37 | | #include "cmList.h" |
38 | | #include "cmListFileCache.h" |
39 | | #include "cmLocalCommonGenerator.h" |
40 | | #include "cmLocalFastbuildGenerator.h" |
41 | | #include "cmLocalGenerator.h" |
42 | | #include "cmMakefile.h" |
43 | | #include "cmOSXBundleGenerator.h" |
44 | | #include "cmObjectLocation.h" |
45 | | #include "cmOutputConverter.h" |
46 | | #include "cmSourceFile.h" |
47 | | #include "cmState.h" |
48 | | #include "cmStateDirectory.h" |
49 | | #include "cmStateSnapshot.h" |
50 | | #include "cmStateTypes.h" |
51 | | #include "cmStringAlgorithms.h" |
52 | | #include "cmSystemTools.h" |
53 | | #include "cmTarget.h" |
54 | | #include "cmTargetDepend.h" |
55 | | #include "cmValue.h" |
56 | | #include "cmake.h" |
57 | | |
58 | | namespace { |
59 | | |
60 | | std::string const COMPILE_DEFINITIONS("COMPILE_DEFINITIONS"); |
61 | | std::string const COMPILE_OPTIONS("COMPILE_OPTIONS"); |
62 | | std::string const COMPILE_FLAGS("COMPILE_FLAGS"); |
63 | | std::string const CMAKE_LANGUAGE("CMAKE"); |
64 | | std::string const INCLUDE_DIRECTORIES("INCLUDE_DIRECTORIES"); |
65 | | |
66 | | std::string const CMAKE_UNITY_BUILD("CMAKE_UNITY_BUILD"); |
67 | | std::string const CMAKE_UNITY_BUILD_BATCH_SIZE("CMAKE_UNITY_BUILD_BATCH_SIZE"); |
68 | | std::string const UNITY_BUILD("UNITY_BUILD"); |
69 | | std::string const UNITY_BUILD_BATCH_SIZE("UNITY_BUILD_BATCH_SIZE"); |
70 | | std::string const SKIP_UNITY_BUILD_INCLUSION("SKIP_UNITY_BUILD_INCLUSION"); |
71 | | std::string const UNITY_GROUP("UNITY_GROUP"); |
72 | | |
73 | | #ifdef _WIN32 |
74 | | char const kPATH_SLASH = '\\'; |
75 | | #else |
76 | | char const kPATH_SLASH = '/'; |
77 | | #endif |
78 | | |
79 | | } // anonymous namespace |
80 | | |
81 | | cmFastbuildNormalTargetGenerator::cmFastbuildNormalTargetGenerator( |
82 | | cmGeneratorTarget* gt, std::string configParam) |
83 | 0 | : cmFastbuildTargetGenerator(gt, std::move(configParam)) |
84 | | , RulePlaceholderExpander( |
85 | 0 | this->LocalCommonGenerator->CreateRulePlaceholderExpander()) |
86 | 0 | , ObjectOutDir(this->GetGlobalGenerator()->ConvertToFastbuildPath( |
87 | 0 | this->GeneratorTarget->GetObjectDirectory(Config))) |
88 | 0 | , Languages(GetLanguages()) |
89 | 0 | , CompileObjectCmakeRules(GetCompileObjectCommand()) |
90 | 0 | , CudaCompileMode(this->GetCudaCompileMode()) |
91 | 0 | { |
92 | |
|
93 | 0 | LogMessage(cmStrCat("objectOutDir: ", ObjectOutDir)); |
94 | 0 | this->OSXBundleGenerator = cm::make_unique<cmOSXBundleGenerator>(gt); |
95 | 0 | this->OSXBundleGenerator->SetMacContentFolders(&this->MacContentFolders); |
96 | | |
97 | | // Quotes to account for potential spaces. |
98 | 0 | RulePlaceholderExpander->SetTargetImpLib( |
99 | 0 | "\"" FASTBUILD_DOLLAR_TAG "TargetOutputImplib" FASTBUILD_DOLLAR_TAG "\""); |
100 | 0 | for (auto const& lang : Languages) { |
101 | 0 | TargetIncludesByLanguage[lang] = this->GetIncludes(lang, Config); |
102 | 0 | LogMessage(cmStrCat("targetIncludes for lang ", lang, " = ", |
103 | 0 | TargetIncludesByLanguage[lang])); |
104 | |
|
105 | 0 | for (auto const& arch : this->GetArches()) { |
106 | 0 | auto& flags = CompileFlagsByLangAndArch[std::make_pair(lang, arch)]; |
107 | 0 | this->LocalCommonGenerator->GetTargetCompileFlags( |
108 | 0 | this->GeneratorTarget, Config, lang, flags, arch); |
109 | 0 | LogMessage( |
110 | 0 | cmStrCat("Lang: ", lang, ", arch: ", arch, ", flags: ", flags)); |
111 | 0 | } |
112 | 0 | } |
113 | 0 | } |
114 | | |
115 | | std::string cmFastbuildNormalTargetGenerator::DetectCompilerFlags( |
116 | | cmSourceFile const& srcFile, std::string const& arch) |
117 | 0 | { |
118 | 0 | std::string const language = srcFile.GetLanguage(); |
119 | 0 | cmGeneratorExpressionInterpreter genexInterpreter( |
120 | 0 | this->GetLocalGenerator(), Config, this->GeneratorTarget, language); |
121 | |
|
122 | 0 | auto const* fileSet = |
123 | 0 | this->GeneratorTarget->GetGeneratorFileSets()->GetFileSetForSource( |
124 | 0 | this->Config, &srcFile); |
125 | |
|
126 | 0 | std::vector<std::string> sourceIncludesVec; |
127 | 0 | if (fileSet) { |
128 | 0 | auto fsIncludes = fileSet->BelongsTo(this->GeneratorTarget) |
129 | 0 | ? fileSet->GetIncludeDirectories(this->Config, language) |
130 | 0 | : fileSet->GetInterfaceIncludeDirectories(this->Config, language); |
131 | 0 | if (!fsIncludes.empty()) { |
132 | 0 | this->LocalGenerator->AppendIncludeDirectories( |
133 | 0 | sourceIncludesVec, cm::remove_BT(fsIncludes), srcFile); |
134 | 0 | } |
135 | 0 | } |
136 | 0 | if (cmValue cincludes = srcFile.GetProperty(INCLUDE_DIRECTORIES)) { |
137 | 0 | this->LocalGenerator->AppendIncludeDirectories( |
138 | 0 | sourceIncludesVec, |
139 | 0 | genexInterpreter.Evaluate(*cincludes, INCLUDE_DIRECTORIES), srcFile); |
140 | 0 | } |
141 | 0 | std::string sourceIncludesStr = this->LocalGenerator->GetIncludeFlags( |
142 | 0 | sourceIncludesVec, this->GeneratorTarget, language, Config, false); |
143 | 0 | LogMessage(cmStrCat("sourceIncludes = ", sourceIncludesStr)); |
144 | |
|
145 | 0 | std::string compileFlags = |
146 | 0 | CompileFlagsByLangAndArch[std::make_pair(language, arch)]; |
147 | 0 | this->GeneratorTarget->AddExplicitLanguageFlags(compileFlags, srcFile); |
148 | |
|
149 | 0 | if (cmValue const cflags = srcFile.GetProperty(COMPILE_FLAGS)) { |
150 | 0 | this->LocalGenerator->AppendFlags( |
151 | 0 | compileFlags, genexInterpreter.Evaluate(*cflags, COMPILE_FLAGS)); |
152 | 0 | } |
153 | |
|
154 | 0 | if (cmValue const coptions = srcFile.GetProperty(COMPILE_OPTIONS)) { |
155 | 0 | this->LocalGenerator->AppendCompileOptions( |
156 | 0 | compileFlags, genexInterpreter.Evaluate(*coptions, COMPILE_OPTIONS)); |
157 | 0 | } |
158 | | // Add flags from file set properties. |
159 | 0 | if (fileSet) { |
160 | 0 | auto options = fileSet->BelongsTo(this->GeneratorTarget) |
161 | 0 | ? fileSet->GetCompileOptions(this->Config, language) |
162 | 0 | : fileSet->GetInterfaceCompileOptions(this->Config, language); |
163 | 0 | if (!options.empty()) { |
164 | 0 | this->LocalGenerator->AppendCompileOptions(compileFlags, |
165 | 0 | cm::remove_BT(options)); |
166 | 0 | } |
167 | 0 | } |
168 | | |
169 | | // Source includes take precedence over target includes. |
170 | 0 | this->LocalGenerator->AppendFlags(compileFlags, sourceIncludesStr); |
171 | 0 | this->LocalGenerator->AppendFlags(compileFlags, |
172 | 0 | TargetIncludesByLanguage[language]); |
173 | |
|
174 | 0 | if (language == "Fortran") { |
175 | 0 | this->AppendFortranFormatFlags(compileFlags, srcFile); |
176 | 0 | this->AppendFortranPreprocessFlags(compileFlags, srcFile); |
177 | 0 | } |
178 | |
|
179 | 0 | LogMessage(cmStrCat("compileFlags = ", compileFlags)); |
180 | 0 | return compileFlags; |
181 | 0 | } |
182 | | |
183 | | void cmFastbuildNormalTargetGenerator::SplitLinkerFromArgs( |
184 | | std::string const& command, std::string& outLinkerExecutable, |
185 | | std::string& outLinkerArgs) const |
186 | 0 | { |
187 | | #ifdef _WIN32 |
188 | | std::vector<std::string> args; |
189 | | std::string tmp; |
190 | | cmSystemTools::SplitProgramFromArgs(command, tmp, outLinkerArgs); |
191 | | // cmLocalGenerator::GetStaticLibraryFlags seems to add empty quotes when |
192 | | // appending "STATIC_LIBRARY_FLAGS_DEBUG"... |
193 | | cmSystemTools::ReplaceString(outLinkerArgs, "\"\"", ""); |
194 | | cmSystemTools::ParseWindowsCommandLine(command.c_str(), args); |
195 | | outLinkerExecutable = std::move(args[0]); |
196 | | #else |
197 | 0 | cmSystemTools::SplitProgramFromArgs(command, outLinkerExecutable, |
198 | 0 | outLinkerArgs); |
199 | 0 | #endif |
200 | 0 | } |
201 | | |
202 | | void cmFastbuildNormalTargetGenerator::GetLinkerExecutableAndArgs( |
203 | | std::string const& command, std::string& outLinkerExecutable, |
204 | | std::string& outLinkerArgs) |
205 | 0 | { |
206 | 0 | if (command.empty()) { |
207 | 0 | return; |
208 | 0 | } |
209 | | |
210 | 0 | LogMessage("Link Command: " + command); |
211 | |
|
212 | 0 | auto const& compilers = this->GetGlobalGenerator()->Compilers; |
213 | 0 | auto const linkerLauncherVarName = FASTBUILD_LINKER_LAUNCHER_PREFIX + |
214 | 0 | this->GeneratorTarget->GetLinkerLanguage(Config); |
215 | 0 | auto const iter = compilers.find(linkerLauncherVarName); |
216 | | // Tested in "RunCMake.LinkerLauncher" test. |
217 | 0 | if (iter != compilers.end()) { |
218 | 0 | LogMessage("Linker launcher: " + iter->first); |
219 | 0 | outLinkerExecutable = iter->second.Executable; |
220 | 0 | outLinkerArgs = cmStrCat(iter->second.Args, ' ', command); |
221 | 0 | } else { |
222 | 0 | SplitLinkerFromArgs(command, outLinkerExecutable, outLinkerArgs); |
223 | 0 | } |
224 | 0 | LogMessage("Linker Exe: " + outLinkerExecutable); |
225 | 0 | LogMessage("Linker args: " + outLinkerArgs); |
226 | 0 | } |
227 | | |
228 | | bool cmFastbuildNormalTargetGenerator::DetectBaseLinkerCommand( |
229 | | std::string& command, std::string const& arch, |
230 | | cmGeneratorTarget::Names const& targetNames) |
231 | 0 | { |
232 | 0 | std::string const linkLanguage = |
233 | 0 | this->GeneratorTarget->GetLinkerLanguage(Config); |
234 | 0 | if (linkLanguage.empty()) { |
235 | 0 | cmSystemTools::Error("CMake can not determine linker language for " |
236 | 0 | "target: " + |
237 | 0 | this->GeneratorTarget->GetName()); |
238 | 0 | return false; |
239 | 0 | } |
240 | 0 | LogMessage("linkLanguage: " + linkLanguage); |
241 | |
|
242 | 0 | std::string linkLibs; |
243 | 0 | std::string targetFlags; |
244 | 0 | std::string linkFlags; |
245 | 0 | std::string frameworkPath; |
246 | | // Tested in "RunCMake.StandardLinkDirectories" test. |
247 | 0 | std::string linkPath; |
248 | |
|
249 | 0 | std::unique_ptr<cmLinkLineComputer> const linkLineComputer = |
250 | 0 | this->GetGlobalGenerator()->CreateLinkLineComputer( |
251 | 0 | this->LocalGenerator, |
252 | 0 | this->GetLocalGenerator()->GetStateSnapshot().GetDirectory()); |
253 | |
|
254 | 0 | this->LocalCommonGenerator->GetTargetFlags( |
255 | 0 | linkLineComputer.get(), Config, linkLibs, targetFlags, linkFlags, |
256 | 0 | frameworkPath, linkPath, this->GeneratorTarget); |
257 | | |
258 | | // cmLocalGenerator::GetStaticLibraryFlags seems to add empty quotes when |
259 | | // appending "STATIC_LIBRARY_FLAGS_DEBUG"... |
260 | 0 | cmSystemTools::ReplaceString(linkFlags, "\"\"", ""); |
261 | 0 | LogMessage("linkLibs: " + linkLibs); |
262 | 0 | LogMessage("targetFlags: " + targetFlags); |
263 | 0 | LogMessage("linkFlags: " + linkFlags); |
264 | 0 | LogMessage("frameworkPath: " + frameworkPath); |
265 | 0 | LogMessage("linkPath: " + linkPath); |
266 | |
|
267 | 0 | LogMessage("MANIFESTS: " + this->GetManifests(Config)); |
268 | |
|
269 | 0 | cmComputeLinkInformation* linkInfo = |
270 | 0 | this->GeneratorTarget->GetLinkInformation(Config); |
271 | 0 | if (!linkInfo) { |
272 | 0 | return false; |
273 | 0 | } |
274 | | |
275 | | // Tested in "RunCMake.RuntimePath" test. |
276 | 0 | std::string const rpath = linkLineComputer->ComputeRPath(*linkInfo); |
277 | 0 | LogMessage("RPath: " + rpath); |
278 | |
|
279 | 0 | if (!linkFlags.empty()) { |
280 | 0 | linkFlags += " "; |
281 | 0 | } |
282 | 0 | linkFlags += cmJoin({ rpath, frameworkPath, linkPath }, " "); |
283 | |
|
284 | 0 | cmStateEnums::TargetType const targetType = this->GeneratorTarget->GetType(); |
285 | | // Add OS X version flags, if any. |
286 | 0 | if (targetType == cmStateEnums::SHARED_LIBRARY || |
287 | 0 | targetType == cmStateEnums::MODULE_LIBRARY) { |
288 | 0 | this->AppendOSXVerFlag(linkFlags, linkLanguage, "COMPATIBILITY", true); |
289 | 0 | this->AppendOSXVerFlag(linkFlags, linkLanguage, "CURRENT", false); |
290 | 0 | } |
291 | | // Add Arch flags to link flags for binaries |
292 | 0 | if (targetType == cmStateEnums::SHARED_LIBRARY || |
293 | 0 | targetType == cmStateEnums::MODULE_LIBRARY || |
294 | 0 | targetType == cmStateEnums::EXECUTABLE) { |
295 | 0 | this->LocalCommonGenerator->AddArchitectureFlags( |
296 | 0 | linkFlags, this->GeneratorTarget, linkLanguage, Config, arch); |
297 | 0 | this->UseLWYU = this->GetLocalGenerator()->AppendLWYUFlags( |
298 | 0 | linkFlags, this->GetGeneratorTarget(), linkLanguage); |
299 | 0 | } |
300 | |
|
301 | 0 | cmRulePlaceholderExpander::RuleVariables vars; |
302 | 0 | vars.CMTargetName = this->GeneratorTarget->GetName().c_str(); |
303 | 0 | vars.CMTargetType = cmState::GetTargetTypeName(targetType).c_str(); |
304 | 0 | vars.Config = Config.c_str(); |
305 | 0 | vars.Language = linkLanguage.c_str(); |
306 | 0 | std::string const manifests = this->GetManifests(Config); |
307 | 0 | vars.Manifests = manifests.c_str(); |
308 | |
|
309 | 0 | std::string const stdLibString = this->Makefile->GetSafeDefinition( |
310 | 0 | cmStrCat("CMAKE_", linkLanguage, "_STANDARD_LIBRARIES")); |
311 | |
|
312 | 0 | LogMessage(cmStrCat("Target type: ", this->GeneratorTarget->GetType())); |
313 | 0 | if (this->GeneratorTarget->GetType() == cmStateEnums::EXECUTABLE || |
314 | 0 | this->GeneratorTarget->GetType() == cmStateEnums::SHARED_LIBRARY || |
315 | 0 | this->GeneratorTarget->GetType() == cmStateEnums::MODULE_LIBRARY) { |
316 | 0 | vars.Objects = FASTBUILD_1_0_INPUT_PLACEHOLDER; |
317 | 0 | vars.LinkLibraries = stdLibString.c_str(); |
318 | 0 | } else { |
319 | 0 | vars.Objects = FASTBUILD_1_INPUT_PLACEHOLDER; |
320 | 0 | } |
321 | |
|
322 | 0 | vars.ObjectDir = FASTBUILD_DOLLAR_TAG "TargetOutDir" FASTBUILD_DOLLAR_TAG; |
323 | 0 | vars.Target = FASTBUILD_2_INPUT_PLACEHOLDER; |
324 | |
|
325 | 0 | std::string install_dir; |
326 | 0 | std::string target_so_name; |
327 | 0 | if (this->GeneratorTarget->HasSOName(Config)) { |
328 | 0 | vars.SONameFlag = this->Makefile->GetSONameFlag( |
329 | 0 | this->GeneratorTarget->GetLinkerLanguage(Config)); |
330 | 0 | target_so_name = |
331 | 0 | cmGlobalFastbuildGenerator::QuoteIfHasSpaces(targetNames.SharedObject); |
332 | 0 | vars.TargetSOName = target_so_name.c_str(); |
333 | | // Tested in "RunCMake.RuntimePath / RunCMake.INSTALL_NAME_DIR" |
334 | | // tests. |
335 | 0 | install_dir = this->LocalGenerator->ConvertToOutputFormat( |
336 | 0 | this->GeneratorTarget->GetInstallNameDirForBuildTree(Config), |
337 | 0 | cmOutputConverter::SHELL); |
338 | 0 | vars.TargetInstallNameDir = install_dir.c_str(); |
339 | 0 | } else { |
340 | 0 | vars.TargetSOName = ""; |
341 | 0 | } |
342 | 0 | vars.TargetPDB = FASTBUILD_DOLLAR_TAG "LinkerPDB" FASTBUILD_DOLLAR_TAG; |
343 | | |
344 | | // Setup the target version. |
345 | 0 | std::string targetVersionMajor; |
346 | 0 | std::string targetVersionMinor; |
347 | 0 | { |
348 | 0 | std::ostringstream majorStream; |
349 | 0 | std::ostringstream minorStream; |
350 | 0 | int major; |
351 | 0 | int minor; |
352 | 0 | this->GeneratorTarget->GetTargetVersion(major, minor); |
353 | 0 | majorStream << major; |
354 | 0 | minorStream << minor; |
355 | 0 | targetVersionMajor = majorStream.str(); |
356 | 0 | targetVersionMinor = minorStream.str(); |
357 | 0 | } |
358 | 0 | vars.TargetVersionMajor = targetVersionMajor.c_str(); |
359 | 0 | vars.TargetVersionMinor = targetVersionMinor.c_str(); |
360 | |
|
361 | 0 | vars.Defines = |
362 | 0 | FASTBUILD_DOLLAR_TAG "CompileDefineFlags" FASTBUILD_DOLLAR_TAG; |
363 | 0 | vars.Flags = targetFlags.c_str(); |
364 | 0 | vars.LinkFlags = linkFlags.c_str(); |
365 | 0 | vars.LanguageCompileFlags = ""; |
366 | 0 | std::string const linker = this->GeneratorTarget->GetLinkerTool(Config); |
367 | 0 | vars.Linker = linker.c_str(); |
368 | 0 | std::string const targetSupportPath = this->ConvertToFastbuildPath( |
369 | 0 | this->GetGeneratorTarget()->GetCMFSupportDirectory()); |
370 | 0 | vars.TargetSupportDir = targetSupportPath.c_str(); |
371 | |
|
372 | 0 | LogMessage("linkFlags: " + linkFlags); |
373 | 0 | LogMessage("linker: " + linker); |
374 | |
|
375 | 0 | std::string linkRule = GetLinkCommand(); |
376 | 0 | ApplyLinkRuleLauncher(linkRule); |
377 | 0 | RulePlaceholderExpander->ExpandRuleVariables( |
378 | 0 | dynamic_cast<cmLocalFastbuildGenerator*>(this->LocalCommonGenerator), |
379 | 0 | linkRule, vars); |
380 | |
|
381 | 0 | command = std::move(linkRule); |
382 | 0 | LogMessage(cmStrCat("Expanded link command: ", command)); |
383 | 0 | return true; |
384 | 0 | } |
385 | | |
386 | | void cmFastbuildNormalTargetGenerator::ApplyLinkRuleLauncher( |
387 | | std::string& command) |
388 | 0 | { |
389 | 0 | std::string const val = this->GetLocalGenerator()->GetRuleLauncher( |
390 | 0 | this->GetGeneratorTarget(), "RULE_LAUNCH_LINK", Config); |
391 | 0 | if (cmNonempty(val)) { |
392 | 0 | LogMessage("RULE_LAUNCH_LINK: " + val); |
393 | 0 | command = cmStrCat(val, ' ', command); |
394 | 0 | } |
395 | 0 | } |
396 | | |
397 | | void cmFastbuildNormalTargetGenerator::ApplyLWYUToLinkerCommand( |
398 | | FastbuildLinkerNode& linkerNode) |
399 | 0 | { |
400 | 0 | cmValue const lwyuCheck = |
401 | 0 | this->Makefile->GetDefinition("CMAKE_LINK_WHAT_YOU_USE_CHECK"); |
402 | 0 | if (this->UseLWYU && lwyuCheck) { |
403 | 0 | LogMessage("UseLWYU=true"); |
404 | 0 | std::string args = " -E __run_co_compile --lwyu="; |
405 | 0 | args += this->GetLocalGenerator()->EscapeForShell(*lwyuCheck); |
406 | |
|
407 | 0 | args += cmStrCat( |
408 | 0 | " --source=", |
409 | 0 | this->ConvertToFastbuildPath(this->GetGeneratorTarget()->GetFullPath( |
410 | 0 | Config, cmStateEnums::RuntimeBinaryArtifact, |
411 | 0 | /*realname=*/true))); |
412 | |
|
413 | 0 | LogMessage("LWUY args: " + args); |
414 | 0 | linkerNode.LinkerStampExe = cmSystemTools::GetCMakeCommand(); |
415 | 0 | linkerNode.LinkerStampExeArgs = std::move(args); |
416 | 0 | } |
417 | 0 | } |
418 | | |
419 | | std::string cmFastbuildNormalTargetGenerator::ComputeDefines( |
420 | | cmSourceFile const& srcFile) |
421 | 0 | { |
422 | 0 | std::string const language = srcFile.GetLanguage(); |
423 | 0 | std::set<std::string> defines; |
424 | 0 | cmGeneratorExpressionInterpreter genexInterpreter( |
425 | 0 | this->GetLocalGenerator(), Config, this->GeneratorTarget, language); |
426 | |
|
427 | 0 | if (auto compile_defs = srcFile.GetProperty(COMPILE_DEFINITIONS)) { |
428 | 0 | this->GetLocalGenerator()->AppendDefines( |
429 | 0 | defines, genexInterpreter.Evaluate(*compile_defs, COMPILE_DEFINITIONS)); |
430 | 0 | } |
431 | |
|
432 | 0 | std::string defPropName = "COMPILE_DEFINITIONS_"; |
433 | 0 | defPropName += cmSystemTools::UpperCase(Config); |
434 | 0 | if (auto config_compile_defs = srcFile.GetProperty(defPropName)) { |
435 | 0 | this->GetLocalGenerator()->AppendDefines( |
436 | 0 | defines, |
437 | 0 | genexInterpreter.Evaluate(*config_compile_defs, COMPILE_DEFINITIONS)); |
438 | 0 | } |
439 | |
|
440 | 0 | if (auto const* fileSet = |
441 | 0 | this->GeneratorTarget->GetGeneratorFileSets()->GetFileSetForSource( |
442 | 0 | this->Config, &srcFile)) { |
443 | 0 | auto fsDefines = fileSet->BelongsTo(this->GeneratorTarget) |
444 | 0 | ? fileSet->GetCompileDefinitions(this->Config, language) |
445 | 0 | : fileSet->GetInterfaceCompileDefinitions(this->Config, language); |
446 | 0 | if (!fsDefines.empty()) { |
447 | 0 | this->LocalGenerator->AppendDefines(defines, fsDefines); |
448 | 0 | } |
449 | 0 | } |
450 | |
|
451 | 0 | std::string definesString = this->GetDefines(language, Config); |
452 | 0 | LogMessage(cmStrCat("TARGET DEFINES = ", definesString)); |
453 | 0 | this->GetLocalGenerator()->JoinDefines(defines, definesString, language); |
454 | |
|
455 | 0 | LogMessage(cmStrCat("DEFINES = ", definesString)); |
456 | 0 | return definesString; |
457 | 0 | } |
458 | | |
459 | | void cmFastbuildNormalTargetGenerator::ComputePCH( |
460 | | cmSourceFile const& srcFile, FastbuildObjectListNode& node, |
461 | | std::set<std::string>& createdPCH) |
462 | 0 | { |
463 | 0 | if (srcFile.GetProperty("SKIP_PRECOMPILE_HEADERS")) { |
464 | 0 | return; |
465 | 0 | } |
466 | | // We have already computed PCH for this node. |
467 | 0 | if (!node.PCHOptions.empty() || !node.PCHInputFile.empty() || |
468 | 0 | !node.PCHOutputFile.empty()) { |
469 | 0 | return; |
470 | 0 | } |
471 | 0 | std::string const language = srcFile.GetLanguage(); |
472 | 0 | cmGeneratorExpressionInterpreter genexInterpreter( |
473 | 0 | this->GetLocalGenerator(), Config, this->GeneratorTarget, language); |
474 | | |
475 | | //.cxx |
476 | 0 | std::string const pchSource = |
477 | 0 | this->GeneratorTarget->GetPchSource(Config, language); |
478 | | //.hxx |
479 | 0 | std::string const pchHeader = |
480 | 0 | this->GeneratorTarget->GetPchHeader(Config, language); |
481 | | //.pch |
482 | 0 | std::string const pchFile = |
483 | 0 | this->GeneratorTarget->GetPchFile(Config, language); |
484 | |
|
485 | 0 | if (pchHeader.empty() || pchFile.empty()) { |
486 | 0 | return; |
487 | 0 | } |
488 | | // In "RunCMake.GenEx-TARGET_PROPERTY" test we call set |
489 | | // CMAKE_PCH_EXTENSION="", so pchHeader becomes same as pchFile... |
490 | 0 | if (pchHeader == pchFile) { |
491 | 0 | LogMessage("pchHeader == pchFile > skipping"); |
492 | 0 | LogMessage("pchHeader: " + pchHeader); |
493 | 0 | LogMessage("pchFile: " + pchFile); |
494 | 0 | return; |
495 | 0 | } |
496 | | |
497 | 0 | node.PCHOutputFile = |
498 | 0 | this->GetGlobalGenerator()->ConvertToFastbuildPath(pchFile); |
499 | | // Tell the ObjectList how to use PCH. |
500 | 0 | std::string const pchUseOption = |
501 | 0 | this->GeneratorTarget->GetPchUseCompileOptions(Config, language); |
502 | 0 | LogMessage(cmStrCat("pchUseOption: ", pchUseOption)); |
503 | |
|
504 | 0 | std::string origCompileOptions = node.CompilerOptions; |
505 | 0 | for (auto const& opt : |
506 | 0 | cmList{ genexInterpreter.Evaluate(pchUseOption, COMPILE_OPTIONS) }) { |
507 | 0 | node.CompilerOptions += " "; |
508 | 0 | node.CompilerOptions += opt; |
509 | 0 | } |
510 | |
|
511 | 0 | if (!createdPCH.emplace(node.PCHOutputFile).second) { |
512 | 0 | LogMessage(node.PCHOutputFile + " is already created by this target"); |
513 | 0 | return; |
514 | 0 | } |
515 | | |
516 | | // Short circuit if the PCH has already been created by another target. |
517 | 0 | if (!this->GeneratorTarget->GetSafeProperty("PRECOMPILE_HEADERS_REUSE_FROM") |
518 | 0 | .empty()) { |
519 | 0 | LogMessage(cmStrCat("PCH: ", node.PCHOutputFile, |
520 | 0 | " already created by another target")); |
521 | 0 | return; |
522 | 0 | } |
523 | | |
524 | 0 | node.PCHInputFile = |
525 | 0 | this->GetGlobalGenerator()->ConvertToFastbuildPath(pchSource); |
526 | |
|
527 | 0 | std::string const pchCreateOptions = |
528 | 0 | this->GeneratorTarget->GetPchCreateCompileOptions(Config, language); |
529 | 0 | LogMessage(cmStrCat("pchCreateOptions: ", pchCreateOptions)); |
530 | 0 | char const* sep = ""; |
531 | 0 | for (auto const& opt : cmList{ |
532 | 0 | genexInterpreter.Evaluate(pchCreateOptions, COMPILE_OPTIONS) }) { |
533 | 0 | node.PCHOptions += sep; |
534 | 0 | node.PCHOptions += opt; |
535 | 0 | sep = " "; |
536 | 0 | } |
537 | | |
538 | | // Reuse compiler options for PCH options. |
539 | 0 | node.PCHOptions += origCompileOptions; |
540 | 0 | if (this->Makefile->GetSafeDefinition("CMAKE_" + language + |
541 | 0 | "_COMPILER_ID") == "MSVC") { |
542 | 0 | cmSystemTools::ReplaceString(node.PCHOptions, |
543 | 0 | FASTBUILD_2_INPUT_PLACEHOLDER, |
544 | 0 | FASTBUILD_3_INPUT_PLACEHOLDER); |
545 | 0 | } |
546 | |
|
547 | 0 | LogMessage("PCH Source: " + pchSource); |
548 | 0 | LogMessage("node.PCHInputFile: " + node.PCHInputFile); |
549 | 0 | LogMessage("node.PCHOutputFile: " + node.PCHOutputFile); |
550 | 0 | LogMessage("node.PCHOptions: " + node.PCHOptions); |
551 | 0 | LogMessage("node.CompilerOptions: " + node.CompilerOptions); |
552 | 0 | } |
553 | | |
554 | | void cmFastbuildNormalTargetGenerator::EnsureDirectoryExists( |
555 | | std::string const& path) const |
556 | 0 | { |
557 | 0 | if (cmSystemTools::FileIsFullPath(path.c_str())) { |
558 | 0 | cmSystemTools::MakeDirectory(path.c_str()); |
559 | 0 | } else { |
560 | 0 | auto* gg = this->GetGlobalGenerator(); |
561 | 0 | std::string fullPath = gg->GetCMakeInstance()->GetHomeOutputDirectory(); |
562 | | // Also ensures there is a trailing slash. |
563 | 0 | fullPath += path; |
564 | 0 | cmSystemTools::MakeDirectory(fullPath); |
565 | 0 | } |
566 | 0 | } |
567 | | |
568 | | void cmFastbuildNormalTargetGenerator::EnsureParentDirectoryExists( |
569 | | std::string const& path) const |
570 | 0 | { |
571 | 0 | this->EnsureDirectoryExists(cmSystemTools::GetParentDirectory(path)); |
572 | 0 | } |
573 | | |
574 | | std::vector<std::string> |
575 | | cmFastbuildNormalTargetGenerator::GetManifestsAsFastbuildPath() const |
576 | 0 | { |
577 | 0 | std::vector<cmSourceFile const*> manifest_srcs; |
578 | 0 | this->GeneratorTarget->GetManifests(manifest_srcs, Config); |
579 | 0 | std::vector<std::string> manifests; |
580 | 0 | manifests.reserve(manifest_srcs.size()); |
581 | 0 | for (auto& manifest_src : manifest_srcs) { |
582 | 0 | std::string str = this->ConvertToFastbuildPath( |
583 | 0 | cmSystemTools::ConvertToOutputPath(manifest_src->GetFullPath())); |
584 | 0 | LogMessage("Manifest: " + str); |
585 | 0 | manifests.emplace_back(std::move(str)); |
586 | 0 | } |
587 | |
|
588 | 0 | return manifests; |
589 | 0 | } |
590 | | |
591 | | void cmFastbuildNormalTargetGenerator::GenerateModuleDefinitionInfo( |
592 | | FastbuildTarget& target) const |
593 | 0 | { |
594 | 0 | cmGeneratorTarget::ModuleDefinitionInfo const* mdi = |
595 | 0 | GeneratorTarget->GetModuleDefinitionInfo(Config); |
596 | 0 | if (mdi && mdi->DefFileGenerated) { |
597 | 0 | FastbuildExecNode execNode; |
598 | 0 | execNode.Name = target.Name + "-def-files"; |
599 | 0 | execNode.ExecExecutable = cmSystemTools::GetCMakeCommand(); |
600 | 0 | execNode.ExecArguments = |
601 | 0 | cmStrCat("-E __create_def ", FASTBUILD_2_INPUT_PLACEHOLDER, ' ', |
602 | 0 | FASTBUILD_1_INPUT_PLACEHOLDER); |
603 | 0 | std::string const obj_list_file = mdi->DefFile + ".objs"; |
604 | |
|
605 | 0 | auto const nm_executable = GetMakefile()->GetDefinition("CMAKE_NM"); |
606 | 0 | if (!nm_executable.IsEmpty()) { |
607 | 0 | execNode.ExecArguments += " --nm="; |
608 | 0 | execNode.ExecArguments += ConvertToFastbuildPath(*nm_executable); |
609 | 0 | } |
610 | 0 | execNode.ExecOutput = ConvertToFastbuildPath(mdi->DefFile); |
611 | 0 | execNode.ExecInput.push_back(ConvertToFastbuildPath(obj_list_file)); |
612 | | |
613 | | // RunCMake.AutoExportDll |
614 | 0 | for (auto const& objList : target.ObjectListNodes) { |
615 | 0 | execNode.PreBuildDependencies.emplace(objList.Name); |
616 | 0 | } |
617 | | // Tested in "RunCMake.AutoExportDll" / "ModuleDefinition" tests. |
618 | 0 | for (auto& linkerNode : target.LinkerNode) { |
619 | 0 | linkerNode.Libraries2.emplace_back(execNode.Name); |
620 | 0 | } |
621 | |
|
622 | 0 | target.PreLinkExecNodes.Nodes.emplace_back(std::move(execNode)); |
623 | | |
624 | | // create a list of obj files for the -E __create_def to read |
625 | 0 | cmGeneratedFileStream fout(obj_list_file); |
626 | | // Since we generate this file once during configuration, we should not |
627 | | // remove it when "clean" is built. |
628 | | // Tested in "RunCMake.AutoExportDll" / "ModuleDefinition" tests. |
629 | 0 | this->GetGlobalGenerator()->AllFilesToKeep.insert(obj_list_file); |
630 | |
|
631 | 0 | if (mdi->WindowsExportAllSymbols) { |
632 | 0 | std::vector<cmSourceFile const*> objectSources; |
633 | 0 | GeneratorTarget->GetObjectSources(objectSources, Config); |
634 | 0 | std::map<cmSourceFile const*, cmObjectLocations> mapping; |
635 | 0 | for (cmSourceFile const* it : objectSources) { |
636 | 0 | mapping[it]; |
637 | 0 | } |
638 | 0 | GeneratorTarget->LocalGenerator->ComputeObjectFilenames(mapping, Config, |
639 | 0 | GeneratorTarget); |
640 | |
|
641 | 0 | std::vector<std::string> objs; |
642 | 0 | for (cmSourceFile const* it : objectSources) { |
643 | 0 | auto const& v = mapping[it]; |
644 | 0 | LogMessage("Obj source : " + v.LongLoc.GetPath()); |
645 | 0 | std::string objFile = this->ConvertToFastbuildPath( |
646 | 0 | GeneratorTarget->GetObjectDirectory(Config) + v.LongLoc.GetPath()); |
647 | 0 | objFile = cmSystemTools::ConvertToOutputPath(objFile); |
648 | 0 | LogMessage("objFile path: " + objFile); |
649 | 0 | objs.push_back(objFile); |
650 | 0 | } |
651 | |
|
652 | 0 | std::vector<cmSourceFile const*> externalObjectSources; |
653 | 0 | GeneratorTarget->GetExternalObjects(externalObjectSources, Config); |
654 | 0 | for (cmSourceFile const* it : externalObjectSources) { |
655 | 0 | objs.push_back(cmSystemTools::ConvertToOutputPath( |
656 | 0 | this->ConvertToFastbuildPath(it->GetFullPath()))); |
657 | 0 | } |
658 | |
|
659 | 0 | for (std::string const& objFile : objs) { |
660 | 0 | if (cmHasLiteralSuffix(objFile, ".obj")) { |
661 | 0 | fout << objFile << "\n"; |
662 | 0 | } |
663 | 0 | } |
664 | 0 | } |
665 | 0 | for (cmSourceFile const* src : mdi->Sources) { |
666 | 0 | fout << src->GetFullPath() << "\n"; |
667 | 0 | } |
668 | 0 | } |
669 | 0 | } |
670 | | |
671 | | void cmFastbuildNormalTargetGenerator::AddPrebuildDeps( |
672 | | FastbuildTarget& target) const |
673 | 0 | { |
674 | | // All ObjectLists should wait for PRE_BUILD. |
675 | 0 | for (FastbuildObjectListNode& node : target.ObjectListNodes) { |
676 | 0 | if (!target.PreBuildExecNodes.Name.empty()) { |
677 | 0 | node.PreBuildDependencies.emplace(target.PreBuildExecNodes.Name); |
678 | 0 | } |
679 | 0 | if (!target.ExecNodes.Name.empty()) { |
680 | 0 | node.PreBuildDependencies.emplace(target.ExecNodes.Name); |
681 | 0 | } |
682 | 0 | } |
683 | 0 | for (auto& linkerNode : target.LinkerNode) { |
684 | | // Wait for 'PRE_BUILD' custom commands. |
685 | 0 | if (!target.PreBuildExecNodes.Name.empty()) { |
686 | 0 | linkerNode.PreBuildDependencies.emplace(target.PreBuildExecNodes.Name); |
687 | 0 | } |
688 | | |
689 | | // Wait for regular custom commands. |
690 | 0 | if (!target.ExecNodes.Name.empty()) { |
691 | 0 | linkerNode.PreBuildDependencies.emplace(target.ExecNodes.Name); |
692 | 0 | } |
693 | | // All targets that we depend on must be prebuilt. |
694 | 0 | if (!target.DependenciesAlias.PreBuildDependencies.empty()) { |
695 | 0 | linkerNode.PreBuildDependencies.emplace(target.DependenciesAlias.Name); |
696 | 0 | } |
697 | 0 | } |
698 | 0 | } |
699 | | |
700 | | std::set<std::string> cmFastbuildNormalTargetGenerator::GetLanguages() |
701 | 0 | { |
702 | 0 | std::set<std::string> result; |
703 | 0 | this->GetGeneratorTarget()->GetLanguages(result, Config); |
704 | 0 | for (std::string const& lang : result) { |
705 | 0 | this->GetGlobalGenerator()->AddCompiler(lang, this->GetMakefile()); |
706 | 0 | } |
707 | 0 | LogMessage("Languages: " + cmJoin(result, ", ")); |
708 | 0 | return result; |
709 | 0 | } |
710 | | |
711 | | std::unordered_map<std::string, std::string> |
712 | | cmFastbuildNormalTargetGenerator::GetCompileObjectCommand() const |
713 | 0 | { |
714 | 0 | std::unordered_map<std::string, std::string> result; |
715 | 0 | result.reserve(Languages.size()); |
716 | 0 | for (std::string const& lang : Languages) { |
717 | 0 | std::vector<std::string> commands; |
718 | 0 | std::string cmakeVar; |
719 | 0 | cmakeVar = "CMAKE_"; |
720 | 0 | cmakeVar += lang; |
721 | 0 | cmakeVar += "_COMPILE_OBJECT"; |
722 | |
|
723 | 0 | std::string cmakeValue = |
724 | 0 | LocalCommonGenerator->GetMakefile()->GetSafeDefinition(cmakeVar); |
725 | |
|
726 | 0 | LogMessage(cmakeVar.append(" = ").append(cmakeValue)); |
727 | |
|
728 | 0 | result[lang] = std::move(cmakeValue); |
729 | 0 | } |
730 | 0 | return result; |
731 | 0 | } |
732 | | std::string cmFastbuildNormalTargetGenerator::GetCudaCompileMode() const |
733 | 0 | { |
734 | 0 | if (Languages.find("CUDA") == Languages.end()) { |
735 | 0 | return {}; |
736 | 0 | } |
737 | | // TODO: unify it with makefile / ninja generators. |
738 | 0 | std::string cudaCompileMode; |
739 | 0 | if (this->GeneratorTarget->GetPropertyAsBool("CUDA_SEPARABLE_COMPILATION")) { |
740 | 0 | std::string const& rdcFlag = |
741 | 0 | this->Makefile->GetRequiredDefinition("_CMAKE_CUDA_RDC_FLAG"); |
742 | 0 | cudaCompileMode = cmStrCat(cudaCompileMode, rdcFlag, ' '); |
743 | 0 | } |
744 | 0 | static std::array<cm::string_view, 4> const compileModes{ |
745 | 0 | { "PTX"_s, "CUBIN"_s, "FATBIN"_s, "OPTIX"_s } |
746 | 0 | }; |
747 | 0 | bool useNormalCompileMode = true; |
748 | 0 | for (cm::string_view mode : compileModes) { |
749 | 0 | auto propName = cmStrCat("CUDA_", mode, "_COMPILATION"); |
750 | 0 | auto defName = cmStrCat("_CMAKE_CUDA_", mode, "_FLAG"); |
751 | 0 | if (this->GeneratorTarget->GetPropertyAsBool(propName)) { |
752 | 0 | std::string const& flag = this->Makefile->GetRequiredDefinition(defName); |
753 | 0 | cudaCompileMode = cmStrCat(cudaCompileMode, flag); |
754 | 0 | useNormalCompileMode = false; |
755 | 0 | break; |
756 | 0 | } |
757 | 0 | } |
758 | 0 | if (useNormalCompileMode) { |
759 | 0 | std::string const& wholeFlag = |
760 | 0 | this->Makefile->GetRequiredDefinition("_CMAKE_CUDA_WHOLE_FLAG"); |
761 | 0 | cudaCompileMode = cmStrCat(cudaCompileMode, wholeFlag); |
762 | 0 | } |
763 | 0 | return cudaCompileMode; |
764 | 0 | } |
765 | | |
766 | | std::string cmFastbuildNormalTargetGenerator::GetLinkCommand() const |
767 | 0 | { |
768 | 0 | std::string const& linkLanguage = GeneratorTarget->GetLinkerLanguage(Config); |
769 | 0 | std::string linkCmdVar = |
770 | 0 | GeneratorTarget->GetCreateRuleVariable(linkLanguage, Config); |
771 | 0 | std::string res = this->Makefile->GetSafeDefinition(linkCmdVar); |
772 | 0 | if (res.empty() && |
773 | 0 | this->GeneratorTarget->GetType() == cmStateEnums::STATIC_LIBRARY) { |
774 | 0 | linkCmdVar = linkCmdVar = |
775 | 0 | cmStrCat("CMAKE_", linkLanguage, "_ARCHIVE_CREATE"); |
776 | 0 | res = this->Makefile->GetSafeDefinition(linkCmdVar); |
777 | 0 | } |
778 | 0 | LogMessage("Link rule: " + cmStrCat(linkCmdVar, " = ", res)); |
779 | 0 | return res; |
780 | 0 | } |
781 | | |
782 | | void cmFastbuildNormalTargetGenerator::AddCompilerLaunchersForLanguages() |
783 | 0 | { |
784 | | // General rule for all languages. |
785 | 0 | std::string const launchCompile = this->GetLocalGenerator()->GetRuleLauncher( |
786 | 0 | this->GetGeneratorTarget(), "RULE_LAUNCH_COMPILE", Config); |
787 | | // See if we need to use a compiler launcher like ccache or distcc |
788 | 0 | for (std::string const& language : Languages) { |
789 | 0 | std::string const compilerLauncher = |
790 | 0 | cmCommonTargetGenerator::GetCompilerLauncher(language, Config); |
791 | 0 | LogMessage("compilerLauncher: " + compilerLauncher); |
792 | 0 | std::vector<std::string> expanded; |
793 | 0 | cmExpandList(compilerLauncher, expanded); |
794 | |
|
795 | 0 | if (!expanded.empty()) { |
796 | 0 | std::string const exe = expanded[0]; |
797 | 0 | expanded.erase(expanded.begin()); |
798 | 0 | this->GetGlobalGenerator()->AddLauncher(FASTBUILD_LAUNCHER_PREFIX, exe, |
799 | 0 | language, cmJoin(expanded, " ")); |
800 | 0 | } else if (!launchCompile.empty()) { |
801 | 0 | std::string exe; |
802 | 0 | std::string args; |
803 | 0 | cmSystemTools::SplitProgramFromArgs(launchCompile, exe, args); |
804 | 0 | this->GetGlobalGenerator()->AddLauncher(FASTBUILD_LAUNCHER_PREFIX, exe, |
805 | 0 | language, args); |
806 | 0 | } |
807 | 0 | } |
808 | 0 | } |
809 | | void cmFastbuildNormalTargetGenerator::AddLinkerLauncher() |
810 | 0 | { |
811 | 0 | std::string const linkerLauncher = |
812 | 0 | cmCommonTargetGenerator::GetLinkerLauncher(Config); |
813 | 0 | std::vector<std::string> args; |
814 | | #ifdef _WIN32 |
815 | | cmSystemTools::ParseWindowsCommandLine(linkerLauncher.c_str(), args); |
816 | | #else |
817 | 0 | cmSystemTools::ParseUnixCommandLine(linkerLauncher.c_str(), args); |
818 | 0 | #endif |
819 | 0 | if (!args.empty()) { |
820 | 0 | std::string const exe = std::move(args[0]); |
821 | 0 | args.erase(args.begin()); |
822 | 0 | this->GetGlobalGenerator()->AddLauncher( |
823 | 0 | FASTBUILD_LINKER_LAUNCHER_PREFIX, exe, |
824 | 0 | this->GeneratorTarget->GetLinkerLanguage(Config), cmJoin(args, " ")); |
825 | 0 | } |
826 | 0 | } |
827 | | void cmFastbuildNormalTargetGenerator::AddCMakeLauncher() |
828 | 0 | { |
829 | | // Add CMake launcher (might be used for static analysis). |
830 | 0 | this->GetGlobalGenerator()->AddLauncher(FASTBUILD_LAUNCHER_PREFIX, |
831 | 0 | cmSystemTools::GetCMakeCommand(), |
832 | 0 | CMAKE_LANGUAGE, ""); |
833 | 0 | } |
834 | | |
835 | | void cmFastbuildNormalTargetGenerator::ComputePaths( |
836 | | FastbuildTarget& target) const |
837 | 0 | { |
838 | 0 | std::string const objPath = GetGeneratorTarget()->GetSupportDirectory(); |
839 | 0 | EnsureDirectoryExists(objPath); |
840 | 0 | target.Variables["TargetOutDir"] = |
841 | 0 | cmSystemTools::ConvertToOutputPath(this->ConvertToFastbuildPath(objPath)); |
842 | |
|
843 | 0 | if (GeneratorTarget->GetType() <= cmStateEnums::MODULE_LIBRARY) { |
844 | 0 | std::string const pdbDir = GeneratorTarget->GetPDBDirectory(Config); |
845 | 0 | LogMessage("GetPDBDirectory: " + pdbDir); |
846 | 0 | EnsureDirectoryExists(pdbDir); |
847 | 0 | std::string const linkerPDB = |
848 | 0 | cmStrCat(pdbDir, '/', this->GeneratorTarget->GetPDBName(Config)); |
849 | |
|
850 | 0 | if (!linkerPDB.empty()) { |
851 | 0 | target.Variables["LinkerPDB"] = cmSystemTools::ConvertToOutputPath( |
852 | 0 | this->ConvertToFastbuildPath(linkerPDB)); |
853 | 0 | } |
854 | 0 | } |
855 | 0 | std::string const compilerPDB = this->ComputeTargetCompilePDB(this->Config); |
856 | 0 | if (!compilerPDB.empty()) { |
857 | 0 | LogMessage("ComputeTargetCompilePDB: " + compilerPDB); |
858 | 0 | std::string compilerPDBArg = cmSystemTools::ConvertToOutputPath( |
859 | 0 | this->ConvertToFastbuildPath(compilerPDB)); |
860 | 0 | if (cmHasSuffix(compilerPDB, '/')) { |
861 | | // The compiler will choose the .pdb file name. |
862 | 0 | this->EnsureDirectoryExists(compilerPDB); |
863 | | // ConvertToFastbuildPath dropped the trailing slash. Add it back. |
864 | | // We do this after ConvertToOutputPath so that we can use a forward |
865 | | // slash in the case that the argument is quoted. |
866 | 0 | if (cmHasSuffix(compilerPDBArg, '"')) { |
867 | | // A quoted trailing backslash requires escaping, e.g., `/Fd"dir\\"`, |
868 | | // but fbuild does not parse such arguments correctly as of 1.15. |
869 | | // Always use a forward slash. |
870 | 0 | compilerPDBArg.insert(compilerPDBArg.size() - 1, 1, '/'); |
871 | 0 | } else { |
872 | | // An unquoted trailing slash or backslash is fine. |
873 | 0 | compilerPDBArg.push_back(kPATH_SLASH); |
874 | 0 | } |
875 | 0 | } else { |
876 | | // We have an explicit .pdb path with file name. |
877 | 0 | this->EnsureParentDirectoryExists(compilerPDB); |
878 | 0 | } |
879 | 0 | target.Variables["CompilerPDB"] = std::move(compilerPDBArg); |
880 | 0 | } |
881 | 0 | std::string const impLibFullPath = |
882 | 0 | GeneratorTarget->GetFullPath(Config, cmStateEnums::ImportLibraryArtifact); |
883 | 0 | std::string impLibFile = ConvertToFastbuildPath(impLibFullPath); |
884 | 0 | cmSystemTools::MakeDirectory(cmSystemTools::GetFilenamePath(impLibFullPath)); |
885 | 0 | if (!impLibFile.empty()) { |
886 | 0 | cmSystemTools::ConvertToOutputSlashes(impLibFile); |
887 | 0 | target.Variables["TargetOutputImplib"] = std::move(impLibFile); |
888 | 0 | } |
889 | 0 | } |
890 | | |
891 | | void cmFastbuildNormalTargetGenerator::Generate() |
892 | 0 | { |
893 | 0 | this->GeneratorTarget->CheckCxxModuleStatus(Config); |
894 | |
|
895 | 0 | FastbuildTarget fastbuildTarget; |
896 | 0 | auto const addUtilDepToTarget = [&fastbuildTarget](std::string depName) { |
897 | 0 | FastbuildTargetDep dep{ depName }; |
898 | 0 | dep.Type = FastbuildTargetDepType::UTIL; |
899 | 0 | fastbuildTarget.PreBuildDependencies.emplace(std::move(dep)); |
900 | 0 | }; |
901 | |
|
902 | 0 | fastbuildTarget.Name = GetTargetName(); |
903 | 0 | fastbuildTarget.BaseName = this->GeneratorTarget->GetName(); |
904 | |
|
905 | 0 | LogMessage("<-------------->"); |
906 | 0 | LogMessage("Generate target: " + fastbuildTarget.Name); |
907 | 0 | LogMessage("Config: " + Config); |
908 | |
|
909 | 0 | LogMessage("Deps: "); |
910 | 0 | for (cmTargetDepend const& dep : TargetDirectDependencies) { |
911 | 0 | auto const tname = dep->GetName(); |
912 | 0 | LogMessage(tname); |
913 | 0 | FastbuildTargetDep targetDep{ tname }; |
914 | 0 | if (dep->GetType() == cmStateEnums::OBJECT_LIBRARY) { |
915 | 0 | targetDep.Type = FastbuildTargetDepType::ORDER_ONLY; |
916 | 0 | } |
917 | 0 | fastbuildTarget.PreBuildDependencies.emplace(std::move(targetDep)); |
918 | 0 | } |
919 | |
|
920 | 0 | ComputePaths(fastbuildTarget); |
921 | 0 | AddCompilerLaunchersForLanguages(); |
922 | 0 | AddLinkerLauncher(); |
923 | 0 | AddCMakeLauncher(); |
924 | |
|
925 | 0 | for (auto& cc : GenerateCommands(FastbuildBuildStep::PRE_BUILD).Nodes) { |
926 | 0 | fastbuildTarget.PreBuildExecNodes.PreBuildDependencies.emplace(cc.Name); |
927 | 0 | addUtilDepToTarget(cc.Name); |
928 | 0 | this->GetGlobalGenerator()->AddTarget(std::move(cc)); |
929 | 0 | } |
930 | 0 | for (auto& cc : GenerateCommands(FastbuildBuildStep::PRE_LINK).Nodes) { |
931 | 0 | cc.PreBuildDependencies.emplace(fastbuildTarget.Name + |
932 | 0 | FASTBUILD_DEPS_ARTIFACTS_ALIAS_POSTFIX); |
933 | 0 | fastbuildTarget.PreLinkExecNodes.Nodes.emplace_back(std::move(cc)); |
934 | 0 | } |
935 | 0 | for (auto& cc : GenerateCommands(FastbuildBuildStep::REST).Nodes) { |
936 | 0 | fastbuildTarget.ExecNodes.PreBuildDependencies.emplace(cc.Name); |
937 | 0 | this->GetGlobalGenerator()->AddTarget(std::move(cc)); |
938 | 0 | } |
939 | 0 | for (auto& cc : GenerateCommands(FastbuildBuildStep::POST_BUILD).Nodes) { |
940 | 0 | fastbuildTarget.PostBuildExecNodes.Alias.PreBuildDependencies.emplace( |
941 | 0 | cc.Name); |
942 | 0 | fastbuildTarget.PostBuildExecNodes.Nodes.emplace_back(std::move(cc)); |
943 | 0 | } |
944 | |
|
945 | 0 | GenerateObjects(fastbuildTarget); |
946 | |
|
947 | 0 | std::vector<std::string> objectDepends; |
948 | 0 | AddObjectDependencies(fastbuildTarget, objectDepends); |
949 | |
|
950 | 0 | GenerateCudaDeviceLink(fastbuildTarget); |
951 | |
|
952 | 0 | GenerateLink(fastbuildTarget, objectDepends); |
953 | |
|
954 | 0 | if (fastbuildTarget.LinkerNode.size() > 1) { |
955 | 0 | if (!this->GeneratorTarget->IsApple()) { |
956 | 0 | cmSystemTools::Error( |
957 | 0 | "Can't handle more than 1 arch on non-Apple target"); |
958 | 0 | return; |
959 | 0 | } |
960 | 0 | AddLipoCommand(fastbuildTarget); |
961 | 0 | } |
962 | 0 | fastbuildTarget.CopyNodes = std::move(this->CopyNodes); |
963 | | |
964 | | // Generate symlink commands if real output name differs from "expected". |
965 | 0 | for (auto& symlink : GetSymlinkExecs()) { |
966 | 0 | fastbuildTarget.PostBuildExecNodes.Alias.PreBuildDependencies.emplace( |
967 | 0 | symlink.Name); |
968 | 0 | fastbuildTarget.PostBuildExecNodes.Nodes.emplace_back(std::move(symlink)); |
969 | 0 | } |
970 | 0 | { |
971 | 0 | auto appleTextStubCommand = GetAppleTextStubCommand(); |
972 | 0 | if (!appleTextStubCommand.Name.empty()) { |
973 | 0 | fastbuildTarget.PostBuildExecNodes.Alias.PreBuildDependencies.emplace( |
974 | 0 | appleTextStubCommand.Name); |
975 | 0 | fastbuildTarget.PostBuildExecNodes.Nodes.emplace_back( |
976 | 0 | std::move(appleTextStubCommand)); |
977 | 0 | } |
978 | 0 | } |
979 | |
|
980 | 0 | AddPrebuildDeps(fastbuildTarget); |
981 | |
|
982 | 0 | fastbuildTarget.IsGlobal = |
983 | 0 | GeneratorTarget->GetType() == cmStateEnums::GLOBAL_TARGET; |
984 | 0 | fastbuildTarget.ExcludeFromAll = |
985 | 0 | this->GetGlobalGenerator()->IsExcluded(GeneratorTarget); |
986 | 0 | if (GeneratorTarget->GetPropertyAsBool("DONT_DISTRIBUTE")) { |
987 | 0 | fastbuildTarget.AllowDistribution = false; |
988 | 0 | } |
989 | |
|
990 | 0 | GenerateModuleDefinitionInfo(fastbuildTarget); |
991 | | // Needs to be called after we've added all PRE-LINK steps (like creation of |
992 | | // .def files on Windows). |
993 | 0 | AddLinkerNodeDependencies(fastbuildTarget); |
994 | | |
995 | | // Must be called after "GenerateObjects", since it also adds Prebuild deps |
996 | | // to it. |
997 | | // Also after "GenerateModuleDefinitionInfo", since uses PreLinkExecNodes. |
998 | |
|
999 | 0 | fastbuildTarget.GenerateAliases(); |
1000 | 0 | if (!fastbuildTarget.ExecNodes.PreBuildDependencies.empty()) { |
1001 | 0 | fastbuildTarget.DependenciesAlias.PreBuildDependencies.emplace( |
1002 | 0 | fastbuildTarget.ExecNodes.Name); |
1003 | 0 | } |
1004 | |
|
1005 | 0 | fastbuildTarget.Hidden = false; |
1006 | |
|
1007 | 0 | fastbuildTarget.BasePath = this->GetMakefile()->GetCurrentSourceDirectory(); |
1008 | |
|
1009 | 0 | this->GetGlobalGenerator()->AddIDEProject(fastbuildTarget, Config); |
1010 | |
|
1011 | 0 | AddStampExeIfApplicable(fastbuildTarget); |
1012 | | |
1013 | | // size 1 means that it's not a multi-arch lib (which can only be the case on |
1014 | | // Darwin). |
1015 | 0 | if (fastbuildTarget.LinkerNode.size() == 1 && |
1016 | 0 | fastbuildTarget.LinkerNode[0].Type == |
1017 | 0 | FastbuildLinkerNode::STATIC_LIBRARY && |
1018 | 0 | !fastbuildTarget.PostBuildExecNodes.Nodes.empty()) { |
1019 | 0 | ProcessPostBuildForStaticLib(fastbuildTarget); |
1020 | 0 | } |
1021 | |
|
1022 | 0 | AdditionalCleanFiles(); |
1023 | |
|
1024 | 0 | if (!fastbuildTarget.DependenciesAlias.PreBuildDependencies.empty()) { |
1025 | 0 | for (FastbuildObjectListNode& objListNode : |
1026 | 0 | fastbuildTarget.ObjectListNodes) { |
1027 | 0 | objListNode.PreBuildDependencies.emplace( |
1028 | 0 | fastbuildTarget.DependenciesAlias.Name); |
1029 | 0 | } |
1030 | 0 | for (auto& linkerNode : fastbuildTarget.LinkerNode) { |
1031 | 0 | linkerNode.PreBuildDependencies.emplace( |
1032 | 0 | fastbuildTarget.DependenciesAlias.Name); |
1033 | 0 | } |
1034 | 0 | } |
1035 | |
|
1036 | 0 | this->GetGlobalGenerator()->AddTarget(std::move(fastbuildTarget)); |
1037 | 0 | } |
1038 | | |
1039 | | void cmFastbuildNormalTargetGenerator::ProcessManifests( |
1040 | | FastbuildLinkerNode& linkerNode) const |
1041 | 0 | { |
1042 | 0 | if (this->GetGlobalGenerator()->GetCMakeInstance()->GetIsInTryCompile()) { |
1043 | 0 | return; |
1044 | 0 | } |
1045 | 0 | std::vector<std::string> const manifests = |
1046 | 0 | this->GetManifestsAsFastbuildPath(); |
1047 | | // Manifests should always be in .Libraries2, so we re-link when needed. |
1048 | | // Tested in RunCMake.BuildDepends |
1049 | 0 | linkerNode.Libraries2.reserve(linkerNode.Libraries2.size() + |
1050 | 0 | manifests.size()); |
1051 | 0 | for (auto const& manifest : manifests) { |
1052 | 0 | linkerNode.Libraries2.emplace_back(manifest); |
1053 | 0 | } |
1054 | 0 | } |
1055 | | |
1056 | | void cmFastbuildNormalTargetGenerator::AddStampExeIfApplicable( |
1057 | | FastbuildTarget& fastbuildTarget) const |
1058 | 0 | { |
1059 | 0 | LogMessage("AddStampExeIfApplicable(...)"); |
1060 | 0 | if (fastbuildTarget.LinkerNode.empty() || |
1061 | 0 | (fastbuildTarget.LinkerNode[0].Type != FastbuildLinkerNode::EXECUTABLE && |
1062 | 0 | fastbuildTarget.LinkerNode[0].Type != |
1063 | 0 | FastbuildLinkerNode::SHARED_LIBRARY)) { |
1064 | 0 | return; |
1065 | 0 | } |
1066 | | // File which executes all POST_BUILD steps. |
1067 | | // We use it in .LinkerStampExeArgs in order to run POST_BUILD steps after |
1068 | | // the compilation. |
1069 | 0 | if (!fastbuildTarget.PostBuildExecNodes.Nodes.empty()) { |
1070 | 0 | std::string const AllPostBuildExecsScriptFile = |
1071 | 0 | cmStrCat(this->Makefile->GetHomeOutputDirectory(), "/CMakeFiles/", |
1072 | 0 | fastbuildTarget.Name, |
1073 | 0 | "-all-postbuild-commands" FASTBUILD_SCRIPT_FILE_EXTENSION); |
1074 | |
|
1075 | 0 | CollapseAllExecsIntoOneScriptfile( |
1076 | 0 | AllPostBuildExecsScriptFile, fastbuildTarget.PostBuildExecNodes.Nodes); |
1077 | 0 | auto& linkerNode = fastbuildTarget.LinkerNode.back(); |
1078 | | // On macOS, a target may have multiple linker nodes (e.g., for different |
1079 | | // architectures). In that case, add the POST_BUILD step to only one node |
1080 | | // to avoid running lipo multiple times. |
1081 | 0 | linkerNode.LinkerStampExe = |
1082 | 0 | cmGlobalFastbuildGenerator::GetExternalShellExecutable(); |
1083 | 0 | linkerNode.LinkerStampExeArgs = FASTBUILD_SCRIPT_FILE_ARG; |
1084 | 0 | linkerNode.LinkerStampExeArgs += |
1085 | 0 | cmGlobalFastbuildGenerator::QuoteIfHasSpaces( |
1086 | 0 | AllPostBuildExecsScriptFile); |
1087 | |
|
1088 | 0 | } else { |
1089 | 0 | LogMessage("No POST_BUILD steps for target: " + fastbuildTarget.Name); |
1090 | 0 | } |
1091 | 0 | } |
1092 | | |
1093 | | void cmFastbuildNormalTargetGenerator::ProcessPostBuildForStaticLib( |
1094 | | FastbuildTarget& fastbuildTarget) const |
1095 | 0 | { |
1096 | | // "Library" nodes do not have "LinkerStampExe" property, so we need to be |
1097 | | // clever here: create an alias that will refer to the binary as well as to |
1098 | | // all post-build steps. Also, make sure that post-build steps depend on the |
1099 | | // binary itself. |
1100 | 0 | LogMessage("ProcessPostBuildForStaticLib(...)"); |
1101 | 0 | FastbuildAliasNode alias; |
1102 | 0 | alias.Name = std::move(fastbuildTarget.LinkerNode[0].Name); |
1103 | 0 | for (FastbuildExecNode& postBuildExec : |
1104 | 0 | fastbuildTarget.PostBuildExecNodes.Nodes) { |
1105 | 0 | postBuildExec.PreBuildDependencies.emplace( |
1106 | 0 | fastbuildTarget.LinkerNode[0].LinkerOutput); |
1107 | 0 | alias.PreBuildDependencies.emplace(postBuildExec.Name); |
1108 | 0 | } |
1109 | 0 | fastbuildTarget.AliasNodes.emplace_back(std::move(alias)); |
1110 | 0 | } |
1111 | | |
1112 | | void cmFastbuildNormalTargetGenerator::CollapseAllExecsIntoOneScriptfile( |
1113 | | std::string const& scriptFileName, |
1114 | | std::vector<FastbuildExecNode> const& execs) const |
1115 | 0 | { |
1116 | 0 | cmsys::ofstream scriptFile(scriptFileName.c_str()); |
1117 | 0 | if (!scriptFile.is_open()) { |
1118 | 0 | cmSystemTools::Error("Failed to open: " + scriptFileName); |
1119 | 0 | return; |
1120 | 0 | } |
1121 | 0 | LogMessage("Writing collapsed Execs to " + scriptFileName); |
1122 | 0 | auto const shell = cmGlobalFastbuildGenerator::GetExternalShellExecutable(); |
1123 | 0 | for (auto const& exec : execs) { |
1124 | 0 | if (exec.ScriptFile.empty()) { |
1125 | 0 | scriptFile << cmSystemTools::ConvertToOutputPath(exec.ExecExecutable) |
1126 | 0 | << " " << exec.ExecArguments << '\n'; |
1127 | 0 | } else { |
1128 | | #if defined(_WIN32) |
1129 | | scriptFile << "call " |
1130 | | << cmSystemTools::ConvertToWindowsOutputPath(exec.ScriptFile) |
1131 | | << '\n'; |
1132 | | #else |
1133 | 0 | scriptFile << cmSystemTools::ConvertToOutputPath(shell) << " " |
1134 | 0 | << cmSystemTools::ConvertToOutputPath(exec.ScriptFile) |
1135 | 0 | << '\n'; |
1136 | 0 | #endif |
1137 | 0 | } |
1138 | 0 | } |
1139 | 0 | } |
1140 | | |
1141 | | std::string cmFastbuildNormalTargetGenerator::ComputeCodeCheckOptions( |
1142 | | cmSourceFile const& srcFile) |
1143 | 0 | { |
1144 | 0 | cmValue const srcSkipCodeCheckVal = srcFile.GetProperty("SKIP_LINTING"); |
1145 | 0 | bool const skipCodeCheck = srcSkipCodeCheckVal.IsSet() |
1146 | 0 | ? srcSkipCodeCheckVal.IsOn() |
1147 | 0 | : this->GetGeneratorTarget()->GetPropertyAsBool("SKIP_LINTING"); |
1148 | |
|
1149 | 0 | if (skipCodeCheck) { |
1150 | 0 | return {}; |
1151 | 0 | } |
1152 | 0 | std::string compilerLauncher; |
1153 | 0 | std::string staticCheckRule = this->GenerateCodeCheckRules( |
1154 | 0 | srcFile, compilerLauncher, "", Config, nullptr); |
1155 | 0 | LogMessage(cmStrCat("CodeCheck: ", staticCheckRule)); |
1156 | 0 | return staticCheckRule; |
1157 | 0 | } |
1158 | | |
1159 | | void cmFastbuildNormalTargetGenerator::ComputeCompilerAndOptions( |
1160 | | std::string const& compilerOptions, std::string const& staticCheckOptions, |
1161 | | std::string const& language, FastbuildObjectListNode& outObjectList) |
1162 | 0 | { |
1163 | 0 | auto& compilers = this->GetGlobalGenerator()->Compilers; |
1164 | 0 | auto const compilerIter = |
1165 | 0 | compilers.find(FASTBUILD_COMPILER_PREFIX + language); |
1166 | 0 | auto const launcherIter = |
1167 | 0 | compilers.find(FASTBUILD_LAUNCHER_PREFIX + language); |
1168 | 0 | if (!staticCheckOptions.empty()) { |
1169 | | // If we want to run static checks - use CMake as a launcher. |
1170 | | // Tested in "RunCMake.ClangTidy", "RunCMake.IncludeWhatYouUse", |
1171 | | // "RunCMake.Cpplint", "RunCMake.Cppcheck", "RunCMake.MultiLint" tests. |
1172 | 0 | outObjectList.Compiler = "." FASTBUILD_LAUNCHER_PREFIX + CMAKE_LANGUAGE; |
1173 | 0 | outObjectList.CompilerOptions = staticCheckOptions; |
1174 | | // Add compile command which will be passed to the static analyzer via |
1175 | | // dash-dash. |
1176 | 0 | if (compilerIter != compilers.end()) { |
1177 | | // Wrap in quotes to account for potential spaces in the path. |
1178 | 0 | outObjectList.CompilerOptions += |
1179 | 0 | cmGlobalFastbuildGenerator::QuoteIfHasSpaces( |
1180 | 0 | compilerIter->second.Executable); |
1181 | 0 | outObjectList.CompilerOptions += compilerOptions; |
1182 | 0 | } |
1183 | 0 | } else if (launcherIter != compilers.end()) { |
1184 | | // Tested in "RunCMake.CompilerLauncher" test. |
1185 | 0 | outObjectList.Compiler = "." + launcherIter->first; |
1186 | 0 | outObjectList.CompilerOptions = launcherIter->second.Args; |
1187 | |
|
1188 | 0 | auto vars = cmFastbuildNormalTargetGenerator::ComputeRuleVariables(); |
1189 | 0 | vars.Language = language.c_str(); |
1190 | 0 | std::string const targetSupportPath = this->ConvertToFastbuildPath( |
1191 | 0 | this->GetGeneratorTarget()->GetCMFSupportDirectory()); |
1192 | 0 | vars.TargetSupportDir = targetSupportPath.c_str(); |
1193 | 0 | RulePlaceholderExpander->ExpandRuleVariables( |
1194 | 0 | LocalCommonGenerator, outObjectList.CompilerOptions, vars); |
1195 | | |
1196 | | // Add compiler executable explicitly to the compile options. |
1197 | 0 | if (compilerIter != compilers.end()) { |
1198 | 0 | outObjectList.CompilerOptions += " "; |
1199 | | // Wrap in quotes to account for potential spaces in the path. |
1200 | 0 | outObjectList.CompilerOptions += |
1201 | 0 | cmGlobalFastbuildGenerator::QuoteIfHasSpaces( |
1202 | 0 | compilerIter->second.Executable); |
1203 | 0 | outObjectList.CompilerOptions += compilerOptions; |
1204 | 0 | } |
1205 | 0 | } else if (compilerIter != compilers.end()) { |
1206 | 0 | outObjectList.Compiler = "." + compilerIter->first; |
1207 | 0 | outObjectList.CompilerOptions = compilerOptions; |
1208 | 0 | } |
1209 | 0 | LogMessage(cmStrCat(".Compiler = ", outObjectList.Compiler)); |
1210 | 0 | LogMessage(cmStrCat(".CompilerOptions = ", outObjectList.CompilerOptions)); |
1211 | 0 | } |
1212 | | |
1213 | | cmRulePlaceholderExpander::RuleVariables |
1214 | | cmFastbuildNormalTargetGenerator::ComputeRuleVariables() const |
1215 | 0 | { |
1216 | 0 | cmRulePlaceholderExpander::RuleVariables compileObjectVars; |
1217 | 0 | compileObjectVars.CMTargetName = GeneratorTarget->GetName().c_str(); |
1218 | 0 | compileObjectVars.CMTargetType = |
1219 | 0 | cmState::GetTargetTypeName(GeneratorTarget->GetType()).c_str(); |
1220 | 0 | compileObjectVars.Source = FASTBUILD_1_INPUT_PLACEHOLDER; |
1221 | 0 | compileObjectVars.Object = FASTBUILD_2_INPUT_PLACEHOLDER; |
1222 | 0 | compileObjectVars.ObjectDir = |
1223 | 0 | FASTBUILD_DOLLAR_TAG "TargetOutDir" FASTBUILD_DOLLAR_TAG; |
1224 | 0 | compileObjectVars.ObjectFileDir = ""; |
1225 | 0 | compileObjectVars.Flags = ""; |
1226 | 0 | compileObjectVars.Includes = ""; |
1227 | 0 | compileObjectVars.Defines = ""; |
1228 | 0 | compileObjectVars.Includes = ""; |
1229 | 0 | compileObjectVars.TargetCompilePDB = |
1230 | 0 | FASTBUILD_DOLLAR_TAG "CompilerPDB" FASTBUILD_DOLLAR_TAG; |
1231 | 0 | compileObjectVars.Config = Config.c_str(); |
1232 | 0 | return compileObjectVars; |
1233 | 0 | } |
1234 | | |
1235 | | std::vector<std::string> cmFastbuildNormalTargetGenerator::GetSourceProperty( |
1236 | | cmSourceFile const& srcFile, std::string const& prop) const |
1237 | 0 | { |
1238 | 0 | std::vector<std::string> res; |
1239 | 0 | if (cmValue val = srcFile.GetProperty(prop)) { |
1240 | 0 | cmExpandList(*val, res); |
1241 | 0 | return GetGlobalGenerator()->ConvertToFastbuildPath(res); |
1242 | 0 | } |
1243 | 0 | return res; |
1244 | 0 | } |
1245 | | |
1246 | | void cmFastbuildNormalTargetGenerator::AppendExtraResources( |
1247 | | std::set<std::string>& deps) const |
1248 | 0 | { |
1249 | | // Generate Fastbuild's "Copy" commands to copy resources. |
1250 | 0 | auto const generateCopyCommands = |
1251 | 0 | [this](std::vector<cmSourceFile const*>& frameworkDeps) { |
1252 | 0 | this->OSXBundleGenerator->GenerateMacOSXContentStatements( |
1253 | 0 | frameworkDeps, this->MacOSXContentGenerator.get(), Config); |
1254 | 0 | }; |
1255 | |
|
1256 | 0 | std::vector<cmSourceFile const*> headerSources; |
1257 | 0 | this->GeneratorTarget->GetHeaderSources(headerSources, Config); |
1258 | 0 | generateCopyCommands(headerSources); |
1259 | |
|
1260 | 0 | std::vector<cmSourceFile const*> extraSources; |
1261 | 0 | this->GeneratorTarget->GetExtraSources(extraSources, Config); |
1262 | 0 | generateCopyCommands(extraSources); |
1263 | |
|
1264 | 0 | std::vector<cmSourceFile const*> externalObjects; |
1265 | 0 | this->GeneratorTarget->GetExternalObjects(externalObjects, Config); |
1266 | 0 | generateCopyCommands(externalObjects); |
1267 | |
|
1268 | 0 | for (FastbuildCopyNode const& node : this->CopyNodes) { |
1269 | 0 | LogMessage("Adding resource: " + node.Name); |
1270 | 0 | deps.emplace(node.Name); |
1271 | 0 | } |
1272 | 0 | } |
1273 | | |
1274 | | std::string cmFastbuildNormalTargetGenerator::GetCompileOptions( |
1275 | | cmSourceFile const& srcFile, std::string const& arch) |
1276 | 0 | { |
1277 | 0 | std::string const language = srcFile.GetLanguage(); |
1278 | 0 | cmRulePlaceholderExpander::RuleVariables compileObjectVars = |
1279 | 0 | ComputeRuleVariables(); |
1280 | 0 | std::string const compilerFlags = DetectCompilerFlags(srcFile, arch); |
1281 | 0 | std::string const compilerDefines = ComputeDefines(srcFile); |
1282 | 0 | compileObjectVars.Flags = compilerFlags.c_str(); |
1283 | 0 | compileObjectVars.Defines = compilerDefines.c_str(); |
1284 | 0 | compileObjectVars.Language = language.c_str(); |
1285 | 0 | if (language == "CUDA") { |
1286 | 0 | compileObjectVars.CudaCompileMode = this->CudaCompileMode.c_str(); |
1287 | 0 | } |
1288 | |
|
1289 | 0 | std::string rule = CompileObjectCmakeRules.at(language); |
1290 | 0 | RulePlaceholderExpander->ExpandRuleVariables(LocalCommonGenerator, rule, |
1291 | 0 | compileObjectVars); |
1292 | |
|
1293 | 0 | std::string compilerExecutable; |
1294 | | // Remove the compiler from .CompilerOptions, since it would be set as |
1295 | | // .Compiler in Fastbuild. |
1296 | | // See https://www.fastbuild.org/docs/functions/objectlist.html for a |
1297 | | // reference. |
1298 | 0 | std::string options; |
1299 | 0 | if (!cmSystemTools::SplitProgramFromArgs(rule, compilerExecutable, |
1300 | 0 | options)) { |
1301 | 0 | cmSystemTools::Error(cmStrCat("Failed to split compiler options: ", rule)); |
1302 | 0 | } |
1303 | 0 | LogMessage("Expanded compile options = " + options); |
1304 | 0 | LogMessage("Compiler executable = " + compilerExecutable); |
1305 | 0 | return options; |
1306 | 0 | } |
1307 | | |
1308 | | std::vector<std::string> cmFastbuildNormalTargetGenerator::GetArches() const |
1309 | 0 | { |
1310 | 0 | auto arches = this->GetGeneratorTarget()->GetAppleArchs(Config, {}); |
1311 | | // Don't add any arch-specific logic if arch is only one. |
1312 | 0 | if (arches.empty() || arches.size() == 1) { |
1313 | 0 | arches.clear(); |
1314 | 0 | arches.emplace_back(); |
1315 | 0 | } |
1316 | 0 | return arches; |
1317 | 0 | } |
1318 | | |
1319 | | void cmFastbuildNormalTargetGenerator::GetCudaDeviceLinkLinkerAndArgs( |
1320 | | std::string& linker, std::string& args) const |
1321 | 0 | { |
1322 | 0 | std::string linkCmd = |
1323 | 0 | this->GetMakefile()->GetDefinition("CMAKE_CUDA_DEVICE_LINK_" |
1324 | 0 | "LIBRARY"); |
1325 | 0 | auto vars = ComputeRuleVariables(); |
1326 | 0 | vars.Language = "CUDA"; |
1327 | 0 | vars.Objects = FASTBUILD_1_INPUT_PLACEHOLDER; |
1328 | 0 | vars.Target = FASTBUILD_2_INPUT_PLACEHOLDER; |
1329 | 0 | std::unique_ptr<cmLinkLineDeviceComputer> linkLineComputer( |
1330 | 0 | new cmLinkLineDeviceComputer( |
1331 | 0 | this->LocalGenerator, |
1332 | 0 | this->LocalGenerator->GetStateSnapshot().GetDirectory())); |
1333 | 0 | std::string linkLibs; |
1334 | 0 | std::string targetFlags; |
1335 | 0 | std::string linkFlags; |
1336 | 0 | std::string frameworkPath; |
1337 | 0 | std::string linkPath; |
1338 | | // So that the call to "GetTargetFlags" does not pollute "LinkLibs" and |
1339 | | // "LinkFlags" with unneeded values. |
1340 | 0 | std::string dummyLinkLibs; |
1341 | 0 | std::string dummyLinkFlags; |
1342 | 0 | this->LocalCommonGenerator->GetDeviceLinkFlags( |
1343 | 0 | *linkLineComputer, Config, linkLibs, linkFlags, frameworkPath, linkPath, |
1344 | 0 | this->GeneratorTarget); |
1345 | 0 | this->LocalCommonGenerator->GetTargetFlags( |
1346 | 0 | linkLineComputer.get(), Config, dummyLinkLibs, targetFlags, dummyLinkFlags, |
1347 | 0 | frameworkPath, linkPath, this->GeneratorTarget); |
1348 | 0 | vars.LanguageCompileFlags = ""; |
1349 | 0 | vars.LinkFlags = linkFlags.c_str(); |
1350 | 0 | vars.LinkLibraries = linkLibs.c_str(); |
1351 | 0 | vars.LanguageCompileFlags = targetFlags.c_str(); |
1352 | 0 | this->RulePlaceholderExpander->ExpandRuleVariables(this->GetLocalGenerator(), |
1353 | 0 | linkCmd, vars); |
1354 | 0 | SplitLinkerFromArgs(linkCmd, linker, args); |
1355 | 0 | } |
1356 | | |
1357 | | void cmFastbuildNormalTargetGenerator::GenerateCudaDeviceLink( |
1358 | | FastbuildTarget& target) const |
1359 | 0 | { |
1360 | 0 | auto const arches = this->GetArches(); |
1361 | 0 | if (!requireDeviceLinking(*this->GeneratorTarget, *this->GetLocalGenerator(), |
1362 | 0 | Config)) { |
1363 | 0 | return; |
1364 | 0 | } |
1365 | 0 | LogMessage("GenerateCudaDeviceLink(...)"); |
1366 | 0 | for (auto const& arch : arches) { |
1367 | 0 | std::string linker; |
1368 | 0 | std::string args; |
1369 | 0 | GetCudaDeviceLinkLinkerAndArgs(linker, args); |
1370 | |
|
1371 | 0 | FastbuildLinkerNode deviceLinkNode; |
1372 | 0 | deviceLinkNode.Name = cmStrCat(target.Name, "_cuda_device_link"); |
1373 | 0 | deviceLinkNode.Type = FastbuildLinkerNode::SHARED_LIBRARY; |
1374 | 0 | deviceLinkNode.Linker = std::move(linker); |
1375 | 0 | deviceLinkNode.LinkerOptions = std::move(args); |
1376 | | // Output |
1377 | 0 | deviceLinkNode.LinkerOutput = this->ConvertToFastbuildPath(cmStrCat( |
1378 | 0 | FASTBUILD_DOLLAR_TAG "TargetOutDi" |
1379 | 0 | "r" FASTBUILD_DOLLAR_TAG "/cmake_device_link", |
1380 | 0 | (args.empty() ? "" : "_" + arch), |
1381 | 0 | this->Makefile->GetSafeDefinition("CMAKE_CUDA_OUTPUT_" |
1382 | 0 | "EXTENSION"))); |
1383 | | |
1384 | | // Input |
1385 | 0 | for (auto const& objList : target.ObjectListNodes) { |
1386 | 0 | deviceLinkNode.LibrarianAdditionalInputs.push_back(objList.Name); |
1387 | 0 | } |
1388 | 0 | target.CudaDeviceLinkNode.emplace_back(std::move(deviceLinkNode)); |
1389 | 0 | } |
1390 | 0 | LogMessage("GenerateCudaDeviceLink end"); |
1391 | 0 | } |
1392 | | |
1393 | | void cmFastbuildNormalTargetGenerator::GenerateObjects(FastbuildTarget& target) |
1394 | 0 | { |
1395 | 0 | this->GetGlobalGenerator()->AllFoldersToClean.insert(ObjectOutDir); |
1396 | |
|
1397 | 0 | std::map<std::string, FastbuildObjectListNode> nodesPermutations; |
1398 | |
|
1399 | 0 | cmCryptoHash hash(cmCryptoHash::AlgoSHA256); |
1400 | |
|
1401 | 0 | std::vector<cmSourceFile const*> objectSources; |
1402 | 0 | GeneratorTarget->GetObjectSources(objectSources, Config); |
1403 | |
|
1404 | 0 | std::set<std::string> createdPCH; |
1405 | | |
1406 | | // Directory level. |
1407 | 0 | bool useUnity = |
1408 | 0 | GeneratorTarget->GetLocalGenerator()->GetMakefile()->IsDefinitionSet( |
1409 | 0 | CMAKE_UNITY_BUILD); |
1410 | | // Check if explicitly disabled for this target. |
1411 | 0 | auto const targetProp = GeneratorTarget->GetProperty(UNITY_BUILD); |
1412 | 0 | if (targetProp.IsSet() && targetProp.IsOff()) { |
1413 | 0 | useUnity = false; |
1414 | 0 | } |
1415 | | |
1416 | | // List of sources isolated from the unity build if enabled. |
1417 | 0 | std::set<std::string> isolatedFromUnity; |
1418 | | |
1419 | | // Mapping from unity group (if any) to sources belonging to that group. |
1420 | 0 | std::map<std::string, std::vector<std::string>> sourcesWithGroups; |
1421 | |
|
1422 | 0 | for (cmSourceFile const* source : objectSources) { |
1423 | |
|
1424 | 0 | cmSourceFile const& srcFile = *source; |
1425 | 0 | std::string const pathToFile = srcFile.GetFullPath(); |
1426 | 0 | bool fileUsesUnity = useUnity; |
1427 | 0 | if (useUnity) { |
1428 | | // Check if the source should be added to "UnityInputIsolatedFiles". |
1429 | 0 | if (srcFile.GetPropertyAsBool(SKIP_UNITY_BUILD_INCLUSION)) { |
1430 | 0 | fileUsesUnity = false; |
1431 | 0 | isolatedFromUnity.emplace(pathToFile); |
1432 | 0 | } |
1433 | 0 | std::string const perFileUnityGroup = |
1434 | 0 | srcFile.GetSafeProperty(UNITY_GROUP); |
1435 | 0 | if (!perFileUnityGroup.empty()) { |
1436 | 0 | sourcesWithGroups[perFileUnityGroup].emplace_back(pathToFile); |
1437 | 0 | } |
1438 | 0 | } |
1439 | |
|
1440 | 0 | this->GetGlobalGenerator()->AddFileToClean(cmStrCat( |
1441 | 0 | ObjectOutDir, '/', this->GeneratorTarget->GetObjectName(source))); |
1442 | | |
1443 | | // Do not generate separate node for PCH source file. |
1444 | 0 | if (this->GeneratorTarget->GetPchSource(Config, srcFile.GetLanguage()) == |
1445 | 0 | pathToFile) { |
1446 | 0 | continue; |
1447 | 0 | } |
1448 | | |
1449 | 0 | std::string const language = srcFile.GetLanguage(); |
1450 | 0 | LogMessage( |
1451 | 0 | cmStrCat("Source file: ", this->ConvertToFastbuildPath(pathToFile))); |
1452 | 0 | LogMessage("Language: " + language); |
1453 | |
|
1454 | 0 | std::string const staticCheckOptions = ComputeCodeCheckOptions(srcFile); |
1455 | |
|
1456 | 0 | auto const isDisabled = [this](char const* prop) { |
1457 | 0 | auto const propValue = this->GeneratorTarget->GetProperty(prop); |
1458 | 0 | return propValue && propValue.IsOff(); |
1459 | 0 | }; |
1460 | 0 | bool const disableCaching = isDisabled("FASTBUILD_CACHING"); |
1461 | 0 | bool const disableDistribution = isDisabled("FASTBUILD_DISTRIBUTION"); |
1462 | |
|
1463 | 0 | for (auto const& arch : this->GetArches()) { |
1464 | 0 | std::string const compileOptions = GetCompileOptions(srcFile, arch); |
1465 | |
|
1466 | 0 | std::string objOutDirWithPossibleSubdir = ObjectOutDir; |
1467 | | |
1468 | | // If object should be placed in some subdir in the output |
1469 | | // path. Tested in "SourceGroups" test. |
1470 | | // Not necessary for files in unity buckets because they are |
1471 | | // built into a single unity object file. Executing this logic |
1472 | | // for unity bucketed files prevents buckets from containing |
1473 | | // source files in different subdirectories. |
1474 | 0 | if (!fileUsesUnity) { |
1475 | 0 | auto const subdir = cmSystemTools::GetFilenamePath( |
1476 | 0 | this->GeneratorTarget->GetObjectName(source)); |
1477 | 0 | if (!subdir.empty()) { |
1478 | 0 | objOutDirWithPossibleSubdir += "/"; |
1479 | 0 | objOutDirWithPossibleSubdir += subdir; |
1480 | 0 | } |
1481 | 0 | } |
1482 | |
|
1483 | 0 | std::string const objectListHash = hash.HashString(cmStrCat( |
1484 | 0 | compileOptions, staticCheckOptions, objOutDirWithPossibleSubdir, |
1485 | | // If file does not need PCH - it must be in another ObjectList. |
1486 | 0 | srcFile.GetProperty("SKIP_PRECOMPILE_HEADERS"), |
1487 | 0 | srcFile.GetLanguage())); |
1488 | |
|
1489 | 0 | LogMessage("ObjectList Hash: " + objectListHash); |
1490 | |
|
1491 | 0 | FastbuildObjectListNode& objectListNode = |
1492 | 0 | nodesPermutations[objectListHash]; |
1493 | | |
1494 | | // Absolute path needed in "RunCMake.SymlinkTrees" test. |
1495 | 0 | objectListNode.CompilerInputFiles.push_back(pathToFile); |
1496 | |
|
1497 | 0 | std::vector<std::string> const outputs = |
1498 | 0 | GetSourceProperty(srcFile, "OBJECT_OUTPUTS"); |
1499 | 0 | objectListNode.ObjectOutputs.insert(outputs.begin(), outputs.end()); |
1500 | |
|
1501 | 0 | std::vector<std::string> const depends = |
1502 | 0 | GetSourceProperty(srcFile, "OBJECT_DEPENDS"); |
1503 | 0 | objectListNode.ObjectDepends.insert(depends.begin(), depends.end()); |
1504 | | |
1505 | | // We have already computed properties that are computed below. |
1506 | | // (.CompilerOptions, .PCH*, etc.). Short circuit this iteration. |
1507 | 0 | if (!objectListNode.CompilerOptions.empty()) { |
1508 | 0 | continue; |
1509 | 0 | } |
1510 | 0 | if (disableCaching) { |
1511 | 0 | objectListNode.AllowCaching = false; |
1512 | 0 | } |
1513 | 0 | if (disableDistribution) { |
1514 | 0 | objectListNode.AllowDistribution = false; |
1515 | 0 | } |
1516 | |
|
1517 | 0 | objectListNode.CompilerOutputPath = objOutDirWithPossibleSubdir; |
1518 | 0 | LogMessage(cmStrCat("Output path: ", objectListNode.CompilerOutputPath)); |
1519 | |
|
1520 | 0 | ComputeCompilerAndOptions(compileOptions, staticCheckOptions, language, |
1521 | 0 | objectListNode); |
1522 | 0 | ComputePCH(*source, objectListNode, createdPCH); |
1523 | |
|
1524 | 0 | objectListNode.Name = cmStrCat(this->GetName(), '_', language, "_Objs"); |
1525 | | // TODO: Ask cmake the output objects and group by extension instead |
1526 | | // of doing this |
1527 | 0 | if (language == "RC") { |
1528 | 0 | objectListNode.CompilerOutputExtension = ".res"; |
1529 | 0 | } else { |
1530 | 0 | if (!arch.empty()) { |
1531 | 0 | objectListNode.CompilerOutputExtension = cmStrCat('.', arch); |
1532 | 0 | objectListNode.arch = arch; |
1533 | 0 | } |
1534 | 0 | char const* customExt = |
1535 | 0 | this->GeneratorTarget->GetCustomObjectExtension(); |
1536 | |
|
1537 | 0 | objectListNode.CompilerOutputExtension += |
1538 | 0 | this->GetMakefile()->GetSafeDefinition( |
1539 | 0 | cmStrCat("CMAKE_", language, "_OUTPUT_EXTENSION")); |
1540 | | // Tested in "CudaOnly.ExportPTX" test. |
1541 | 0 | if (customExt) { |
1542 | 0 | objectListNode.CompilerOutputExtension += customExt; |
1543 | 0 | } |
1544 | 0 | } |
1545 | 0 | } |
1546 | 0 | } |
1547 | |
|
1548 | 0 | int groupNameCount = 0; |
1549 | |
|
1550 | 0 | for (auto& val : nodesPermutations) { |
1551 | 0 | auto& objectListNode = val.second; |
1552 | 0 | objectListNode.Name = cmStrCat(objectListNode.Name, '_', ++groupNameCount); |
1553 | 0 | LogMessage(cmStrCat("ObjectList name: ", objectListNode.Name)); |
1554 | 0 | } |
1555 | 0 | std::vector<FastbuildObjectListNode>& objects = target.ObjectListNodes; |
1556 | 0 | objects.reserve(nodesPermutations.size()); |
1557 | 0 | for (auto& val : nodesPermutations) { |
1558 | 0 | auto& node = val.second; |
1559 | 0 | if (!node.PCHInputFile.empty()) { |
1560 | | // Node that produces PCH should be the first one, since other nodes |
1561 | | // might reuse this PCH. |
1562 | | // Note: we might have several such nodes for different languages. |
1563 | 0 | objects.insert(objects.begin(), std::move(node)); |
1564 | 0 | } else { |
1565 | 0 | objects.emplace_back(std::move(node)); |
1566 | 0 | } |
1567 | 0 | } |
1568 | 0 | if (useUnity) { |
1569 | 0 | target.UnityNodes = |
1570 | 0 | GenerateUnity(objects, isolatedFromUnity, sourcesWithGroups); |
1571 | 0 | } |
1572 | 0 | } |
1573 | | |
1574 | | FastbuildUnityNode cmFastbuildNormalTargetGenerator::GetOneUnity( |
1575 | | std::set<std::string> const& isolatedFiles, std::vector<std::string>& files, |
1576 | | int unitySize) const |
1577 | 0 | { |
1578 | 0 | FastbuildUnityNode result; |
1579 | 0 | for (auto iter = files.begin(); iter != files.end();) { |
1580 | 0 | std::string pathToFile = std::move(*iter); |
1581 | 0 | iter = files.erase(iter); |
1582 | | // This source must be isolated |
1583 | 0 | if (isolatedFiles.find(pathToFile) != isolatedFiles.end()) { |
1584 | 0 | result.UnityInputFiles.emplace_back(pathToFile); |
1585 | 0 | result.UnityInputIsolatedFiles.emplace_back(std::move(pathToFile)); |
1586 | 0 | } else { |
1587 | 0 | result.UnityInputFiles.emplace_back(std::move(pathToFile)); |
1588 | 0 | } |
1589 | 0 | if (int(result.UnityInputFiles.size() - |
1590 | 0 | result.UnityInputIsolatedFiles.size()) == unitySize) { |
1591 | 0 | break; |
1592 | 0 | } |
1593 | 0 | } |
1594 | 0 | return result; |
1595 | 0 | } |
1596 | | int cmFastbuildNormalTargetGenerator::GetUnityBatchSize() const |
1597 | 0 | { |
1598 | 0 | int unitySize = 8; |
1599 | 0 | try { |
1600 | 0 | auto const perTargetSize = |
1601 | 0 | GeneratorTarget->GetSafeProperty(UNITY_BUILD_BATCH_SIZE); |
1602 | 0 | if (!perTargetSize.empty()) { |
1603 | 0 | unitySize = std::stoi(perTargetSize); |
1604 | 0 | } |
1605 | | // Per-directory level. |
1606 | 0 | else { |
1607 | 0 | unitySize = std::stoi( |
1608 | 0 | GeneratorTarget->GetLocalGenerator()->GetMakefile()->GetDefinition( |
1609 | 0 | CMAKE_UNITY_BUILD_BATCH_SIZE)); |
1610 | 0 | } |
1611 | 0 | } catch (...) { |
1612 | 0 | return unitySize; |
1613 | 0 | } |
1614 | 0 | return unitySize; |
1615 | 0 | } |
1616 | | |
1617 | | std::vector<FastbuildUnityNode> |
1618 | | cmFastbuildNormalTargetGenerator::GenerateUnity( |
1619 | | std::vector<FastbuildObjectListNode>& objects, |
1620 | | std::set<std::string> const& isolatedSources, |
1621 | | std::map<std::string, std::vector<std::string>> const& sourcesWithGroups) |
1622 | 0 | { |
1623 | 0 | int const unitySize = GetUnityBatchSize(); |
1624 | | // Unity of size less than 2 doesn't make sense. |
1625 | 0 | if (unitySize < 2) { |
1626 | 0 | return {}; |
1627 | 0 | } |
1628 | | |
1629 | 0 | int unityNumber = 0; |
1630 | 0 | int unityGroupNumber = 0; |
1631 | 0 | std::vector<FastbuildUnityNode> result; |
1632 | |
|
1633 | 0 | for (FastbuildObjectListNode& obj : objects) { |
1634 | | // Don't use unity for only 1 file. |
1635 | 0 | if (obj.CompilerInputFiles.size() < 2) { |
1636 | 0 | continue; |
1637 | 0 | } |
1638 | 0 | std::string const ext = |
1639 | 0 | cmSystemTools::GetFilenameExtension(obj.CompilerInputFiles[0]); |
1640 | | // Process groups. |
1641 | 0 | auto groupedNode = GenerateGroupedUnityNode( |
1642 | 0 | obj.CompilerInputFiles, sourcesWithGroups, unityGroupNumber); |
1643 | | // We have at least 2 sources in the group. |
1644 | 0 | if (groupedNode.UnityInputFiles.size() > 1) { |
1645 | 0 | groupedNode.UnityOutputPath = obj.CompilerOutputPath; |
1646 | 0 | obj.CompilerInputUnity.emplace_back(groupedNode.Name); |
1647 | 0 | groupedNode.UnityOutputPattern = cmStrCat(groupedNode.Name, ext); |
1648 | 0 | result.emplace_back(std::move(groupedNode)); |
1649 | 0 | } |
1650 | | // General unity batching of the remaining (non-grouped) sources. |
1651 | 0 | while (!obj.CompilerInputFiles.empty()) { |
1652 | 0 | FastbuildUnityNode node = |
1653 | 0 | GetOneUnity(isolatedSources, obj.CompilerInputFiles, unitySize); |
1654 | 0 | node.Name = cmStrCat(this->GetName(), "_Unity_", ++unityNumber); |
1655 | 0 | node.UnityOutputPath = obj.CompilerOutputPath; |
1656 | 0 | node.UnityOutputPattern = cmStrCat(node.Name, ext); |
1657 | | |
1658 | | // Unity group of size 1 doesn't make sense - just isolate the source. |
1659 | 0 | if (groupedNode.UnityInputFiles.size() == 1) { |
1660 | 0 | node.UnityInputIsolatedFiles.emplace_back( |
1661 | 0 | groupedNode.UnityInputFiles[0]); |
1662 | 0 | node.UnityInputFiles.emplace_back( |
1663 | 0 | std::move(groupedNode.UnityInputFiles[0])); |
1664 | | // Clear so we don't enter here on the next iteration. |
1665 | 0 | groupedNode.UnityInputFiles.clear(); |
1666 | 0 | } |
1667 | | |
1668 | | // We've got only 1 file left. No need to create a Unity node for it, |
1669 | | // just return it back to the ObjectList and exit. |
1670 | 0 | if (node.UnityInputFiles.size() == 1) { |
1671 | 0 | obj.CompilerInputFiles.emplace_back( |
1672 | 0 | std::move(node.UnityInputFiles[0])); |
1673 | 0 | break; |
1674 | 0 | } |
1675 | | |
1676 | 0 | obj.CompilerInputUnity.emplace_back(node.Name); |
1677 | 0 | result.emplace_back(std::move(node)); |
1678 | 0 | } |
1679 | 0 | } |
1680 | 0 | return result; |
1681 | 0 | } |
1682 | | |
1683 | | FastbuildUnityNode cmFastbuildNormalTargetGenerator::GenerateGroupedUnityNode( |
1684 | | std::vector<std::string>& inputFiles, |
1685 | | std::map<std::string, std::vector<std::string>> const& sourcesWithGroups, |
1686 | | int& groupId) |
1687 | 0 | { |
1688 | 0 | std::vector<FastbuildUnityNode> result; |
1689 | 0 | for (auto const& item : sourcesWithGroups) { |
1690 | 0 | auto const& group = item.first; |
1691 | 0 | auto const& sources = item.second; |
1692 | 0 | FastbuildUnityNode node; |
1693 | | // Check if any of the sources belong to this group. |
1694 | 0 | for (auto const& source : sources) { |
1695 | 0 | auto const iter = |
1696 | 0 | std::find(inputFiles.begin(), inputFiles.end(), source); |
1697 | 0 | if (iter == inputFiles.end()) { |
1698 | 0 | continue; |
1699 | 0 | } |
1700 | 0 | node.Name = |
1701 | 0 | cmStrCat(this->GetName(), "_Unity_Group_", group, '_', ++groupId); |
1702 | 0 | node.UnityInputFiles.emplace_back(source); |
1703 | | |
1704 | | // Remove from the general batching. |
1705 | 0 | inputFiles.erase( |
1706 | 0 | std::remove(inputFiles.begin(), inputFiles.end(), source), |
1707 | 0 | inputFiles.end()); |
1708 | 0 | } |
1709 | 0 | if (!node.UnityInputFiles.empty()) { |
1710 | | // The unity group belongs to the ObjectLists that we're processing. |
1711 | | // We've grouped all the sources we could from the current ObjectList. |
1712 | 0 | return node; |
1713 | 0 | } |
1714 | 0 | } |
1715 | 0 | return {}; |
1716 | 0 | } |
1717 | | |
1718 | | std::string cmFastbuildNormalTargetGenerator::ResolveIfAlias( |
1719 | | std::string const& targetName) const |
1720 | 0 | { |
1721 | 0 | LogMessage("targetName: " + targetName); |
1722 | 0 | std::map<std::string, std::string> const aliases = |
1723 | 0 | this->Makefile->GetAliasTargets(); |
1724 | 0 | auto const iter = aliases.find(targetName); |
1725 | 0 | if (iter != aliases.end()) { |
1726 | 0 | LogMessage("Non alias name: " + iter->second); |
1727 | 0 | return iter->second; |
1728 | 0 | } |
1729 | 0 | return targetName; |
1730 | 0 | } |
1731 | | |
1732 | | void cmFastbuildNormalTargetGenerator::AppendExternalObject( |
1733 | | FastbuildLinkerNode& linkerNode, std::set<std::string>& linkedDeps) const |
1734 | 0 | { |
1735 | | // Different aspects of this logic exercised in "ObjectLibrary" and |
1736 | | // "ExportImport" test. When making changes here - verify that both of those |
1737 | | // tests are still passing. |
1738 | 0 | LogMessage("AppendExternalObject(...)"); |
1739 | 0 | std::vector<cmSourceFile const*> extObjects; |
1740 | 0 | this->GeneratorTarget->GetExternalObjects(extObjects, Config); |
1741 | 0 | for (cmSourceFile const* src : extObjects) { |
1742 | |
|
1743 | 0 | std::string const pathToObj = |
1744 | 0 | this->ConvertToFastbuildPath(src->GetFullPath()); |
1745 | 0 | LogMessage("EXT OBJ: " + pathToObj); |
1746 | 0 | std::string const objLibName = ResolveIfAlias(src->GetObjectLibrary()); |
1747 | 0 | LogMessage("GetObjectLibrary: " + objLibName); |
1748 | | // Tested in "ExternalOBJ" test. |
1749 | 0 | cmTarget const* target = |
1750 | 0 | this->GlobalCommonGenerator->FindTarget(objLibName); |
1751 | 0 | if (objLibName.empty()) { |
1752 | 0 | linkerNode.LibrarianAdditionalInputs.emplace_back(pathToObj); |
1753 | 0 | } |
1754 | | // We know how to generate this target and haven't added this dependency |
1755 | | // yet. |
1756 | 0 | else if (target) { |
1757 | 0 | if (!linkedDeps.emplace(objLibName + FASTBUILD_OBJECTS_ALIAS_POSTFIX) |
1758 | 0 | .second) { |
1759 | 0 | LogMessage("Object Target: " + objLibName + |
1760 | 0 | FASTBUILD_OBJECTS_ALIAS_POSTFIX " already linked"); |
1761 | 0 | continue; |
1762 | 0 | } |
1763 | 0 | linkerNode.LibrarianAdditionalInputs.emplace_back( |
1764 | 0 | objLibName + FASTBUILD_OBJECTS_ALIAS_POSTFIX); |
1765 | 0 | } else if (linkedDeps.emplace(pathToObj).second) { |
1766 | 0 | LogMessage("Adding obj dep : " + pathToObj); |
1767 | 0 | linkerNode.LibrarianAdditionalInputs.emplace_back(pathToObj); |
1768 | 0 | } |
1769 | 0 | } |
1770 | 0 | } |
1771 | | |
1772 | | void cmFastbuildNormalTargetGenerator::AppendExeToLink( |
1773 | | FastbuildLinkerNode& linkerNode, |
1774 | | cmComputeLinkInformation::Item const& item) const |
1775 | 0 | { |
1776 | 0 | std::string const decorated = |
1777 | 0 | item.GetFormattedItem(this->ConvertToFastbuildPath(item.Value.Value)) |
1778 | 0 | .Value; |
1779 | 0 | LogMessage("Linking to executable : " + decorated); |
1780 | | // Tested in "InterfaceLinkLibrariesDirect" and "Plugin" test. |
1781 | 0 | linkerNode.LinkerOptions += |
1782 | 0 | (" " + cmGlobalFastbuildGenerator::QuoteIfHasSpaces(decorated)); |
1783 | 0 | } |
1784 | | |
1785 | | std::string cmFastbuildNormalTargetGenerator::GetImportedLoc( |
1786 | | cmComputeLinkInformation::Item const& item) const |
1787 | 0 | { |
1788 | | // Link to import library when possible. |
1789 | | // Tested in "StagingPrefix" test on Windows/MSVC. |
1790 | 0 | cmStateEnums::ArtifactType const artifact = |
1791 | 0 | item.Target->HasImportLibrary(Config) |
1792 | 0 | ? cmStateEnums::ImportLibraryArtifact |
1793 | 0 | : cmStateEnums::RuntimeBinaryArtifact; |
1794 | |
|
1795 | 0 | std::string importedLoc = this->ConvertToFastbuildPath( |
1796 | 0 | item.Target->GetFullPath(Config, artifact, true)); |
1797 | 0 | LogMessage("ImportedGetLocation: " + importedLoc); |
1798 | 0 | return importedLoc; |
1799 | 0 | } |
1800 | | |
1801 | | void cmFastbuildNormalTargetGenerator::AppendTargetDep( |
1802 | | FastbuildLinkerNode& linkerNode, std::set<std::string>& linkedObjects, |
1803 | | cmComputeLinkInformation::Item const& item) const |
1804 | 0 | { |
1805 | 0 | LogMessage("AppendTargetDep(...)"); |
1806 | 0 | cmStateEnums::TargetType const depType = item.Target->GetType(); |
1807 | 0 | LogMessage("Link dep type: " + std::to_string(depType)); |
1808 | 0 | LogMessage("Target name: " + item.Target->GetName()); |
1809 | 0 | auto const resolvedTargetName = ResolveIfAlias(item.Target->GetName()); |
1810 | 0 | LogMessage("Resolved: " + resolvedTargetName); |
1811 | 0 | if (depType == cmStateEnums::INTERFACE_LIBRARY) { |
1812 | 0 | return; |
1813 | 0 | } |
1814 | 0 | std::string const feature = item.GetFeatureName(); |
1815 | |
|
1816 | 0 | if (item.Target->IsImported()) { |
1817 | |
|
1818 | 0 | if (feature == "FRAMEWORK") { |
1819 | | // Use just framework's name. The exact path where to look for the |
1820 | | // framework will be provided from "frameworkPath" in |
1821 | | // "cmFastbuildNormalTargetGenerator::DetectBaseLinkerCommand(...)". |
1822 | | // Tested in "RunCMake.Framework - ImportedFrameworkConsumption". |
1823 | 0 | std::string const decorated = |
1824 | 0 | item.GetFormattedItem(item.Value.Value).Value; |
1825 | 0 | LogMessage( |
1826 | 0 | cmStrCat("Adding framework dep <", decorated, "> to command line")); |
1827 | 0 | linkerNode.LinkerOptions += (" " + decorated); |
1828 | 0 | return; |
1829 | 0 | } |
1830 | 0 | if (depType == cmStateEnums::UNKNOWN_LIBRARY) { |
1831 | 0 | LogMessage("Unknown library -- adding to LibrarianAdditionalInputs or " |
1832 | 0 | "Libraries2"); |
1833 | 0 | if (UsingCommandLine) { |
1834 | 0 | AppendCommandLineDep(linkerNode, item); |
1835 | 0 | } else { |
1836 | 0 | AppendLinkDep(linkerNode, GetImportedLoc(item)); |
1837 | 0 | } |
1838 | 0 | return; |
1839 | 0 | } |
1840 | | // Tested in "ExportImport" test. |
1841 | 0 | if (depType == cmStateEnums::EXECUTABLE) { |
1842 | 0 | AppendExeToLink(linkerNode, item); |
1843 | 0 | return; |
1844 | 0 | } |
1845 | | // Skip exported objects. |
1846 | | // Tested in "ExportImport" test. |
1847 | 0 | if (depType == cmStateEnums::OBJECT_LIBRARY) { |
1848 | 0 | LogMessage("target : " + item.Target->GetName() + |
1849 | 0 | " already linked... Skipping"); |
1850 | 0 | return; |
1851 | 0 | } |
1852 | | // Tested in "ExportImport" test. |
1853 | 0 | cmList const list{ GetImportedLoc(item) }; |
1854 | 0 | for (std::string const& linkDep : list) { |
1855 | 0 | AppendLinkDep(linkerNode, linkDep); |
1856 | 0 | } |
1857 | 0 | } else { |
1858 | 0 | if (depType == cmStateEnums::SHARED_LIBRARY && |
1859 | 0 | this->GeneratorTarget->GetPropertyAsBool("LINK_DEPENDS_NO_SHARED")) { |
1860 | | // It moves the dep outside of FASTBuild control, so the binary won't |
1861 | | // be re-built if the shared lib has changed. |
1862 | | // Tested in "BuildDepends" test. |
1863 | 0 | LogMessage( |
1864 | 0 | cmStrCat("LINK_DEPENDS_NO_SHARED is set on the target, adding dep", |
1865 | 0 | item.Value.Value, " as is")); |
1866 | 0 | linkerNode.LinkerOptions += |
1867 | 0 | (" " + cmGlobalFastbuildGenerator::QuoteIfHasSpaces(item.Value.Value)); |
1868 | 0 | return; |
1869 | 0 | } |
1870 | | // Just add path to binary artifact to command line (except for OBJECT |
1871 | | // libraries which we will link directly). |
1872 | 0 | if (UsingCommandLine && depType != cmStateEnums::OBJECT_LIBRARY) { |
1873 | | // Take transitively linked objects into account, |
1874 | | // so we don't link them again. |
1875 | 0 | AppendTransitivelyLinkedObjects(*item.Target, linkedObjects); |
1876 | 0 | AppendCommandLineDep(linkerNode, item); |
1877 | 0 | return; |
1878 | 0 | } |
1879 | | // This dep has a special way of linking to it (e.g. |
1880 | | // "CMAKE_LINK_LIBRARY_USING_<FEATURE>"). |
1881 | 0 | bool const isFeature = !feature.empty() && feature != "DEFAULT"; |
1882 | 0 | if (isFeature) { |
1883 | 0 | std::string const decorated = |
1884 | 0 | item.GetFormattedItem(this->ConvertToFastbuildPath(item.Value.Value)) |
1885 | 0 | .Value; |
1886 | 0 | LogMessage("Prepending with feature: " + decorated); |
1887 | 0 | linkerNode.LinkerOptions += (" " + decorated); |
1888 | 0 | } |
1889 | |
|
1890 | 0 | std::string dep = resolvedTargetName + |
1891 | 0 | (depType == cmStateEnums::OBJECT_LIBRARY |
1892 | 0 | ? FASTBUILD_OBJECTS_ALIAS_POSTFIX |
1893 | 0 | : FASTBUILD_LINK_ARTIFACTS_ALIAS_POSTFIX); |
1894 | 0 | if (!linkerNode.Arch.empty()) { |
1895 | 0 | dep += cmStrCat('-', linkerNode.Arch); |
1896 | 0 | } |
1897 | | // If we have a special way of linking the dep, we can't have it in |
1898 | | // ".Libraries" (since there might be multiple such deps, but |
1899 | | // FASTBuild expands ".Libraries" as a continuous array, so we can't |
1900 | | // inject any properties in between). Tested in |
1901 | | // "RunCMake.target_link_libraries-LINK_LIBRARY" test. |
1902 | 0 | if (isFeature) { |
1903 | 0 | LogMessage(cmStrCat("AppendTargetDep: ", dep, " as prebuild")); |
1904 | 0 | linkerNode.PreBuildDependencies.emplace(dep); |
1905 | 0 | return; |
1906 | 0 | } |
1907 | | |
1908 | 0 | if (depType != cmStateEnums::OBJECT_LIBRARY || |
1909 | 0 | linkedObjects.emplace(dep).second) { |
1910 | 0 | AppendLinkDep(linkerNode, dep); |
1911 | 0 | } |
1912 | 0 | AppendTransitivelyLinkedObjects(*item.Target, linkedObjects); |
1913 | 0 | } |
1914 | 0 | } |
1915 | | |
1916 | | void cmFastbuildNormalTargetGenerator::AppendPrebuildDeps( |
1917 | | FastbuildLinkerNode& linkerNode, |
1918 | | cmComputeLinkInformation::Item const& item) const |
1919 | 0 | { |
1920 | 0 | if (!item.Target->IsImported()) { |
1921 | 0 | return; |
1922 | 0 | } |
1923 | | // In "RunCMake.FileAPI" imported object library "imported_object_lib" is |
1924 | | // added w/o import location... |
1925 | 0 | if (item.Target->GetType() == cmStateEnums::OBJECT_LIBRARY) { |
1926 | 0 | return; |
1927 | 0 | } |
1928 | 0 | cmList const list{ GetImportedLoc(item) }; |
1929 | 0 | for (std::string const& linkDep : list) { |
1930 | | // In case we know how to generate this file (needed for proper |
1931 | | // sorting by deps). Tested in "RunCMake.target_link_libraries-ALIAS" |
1932 | | // test. |
1933 | 0 | auto fastbuildTarget = |
1934 | 0 | this->GetGlobalGenerator()->GetTargetByOutputName(linkDep); |
1935 | 0 | std::string fastbuildTargetName; |
1936 | 0 | if (fastbuildTarget) { |
1937 | 0 | fastbuildTargetName = std::move(fastbuildTarget->Name); |
1938 | 0 | } |
1939 | 0 | if (!fastbuildTargetName.empty()) { |
1940 | 0 | LogMessage("Adding dep to " + fastbuildTargetName); |
1941 | 0 | linkerNode.PreBuildDependencies.insert(std::move(fastbuildTargetName)); |
1942 | 0 | } else { |
1943 | 0 | if (!cmIsNOTFOUND(linkDep)) { |
1944 | 0 | LogMessage(cmStrCat("Adding dep ", linkDep, " for sorting")); |
1945 | 0 | linkerNode.PreBuildDependencies.insert(linkDep); |
1946 | 0 | } |
1947 | 0 | } |
1948 | 0 | } |
1949 | 0 | } |
1950 | | |
1951 | | void cmFastbuildNormalTargetGenerator::AppendTransitivelyLinkedObjects( |
1952 | | cmGeneratorTarget const& target, std::set<std::string>& linkedObjects) const |
1953 | 0 | { |
1954 | 0 | std::vector<std::string> objs; |
1955 | | // Consider that all those object are now linked as well. |
1956 | | // Tested in "ExportImport" test. |
1957 | 0 | target.GetTargetObjectNames(Config, objs); |
1958 | 0 | for (std::string const& obj : objs) { |
1959 | 0 | std::string const pathToObj = this->ConvertToFastbuildPath( |
1960 | 0 | cmStrCat(target.GetObjectDirectory(Config), '/', obj)); |
1961 | 0 | linkedObjects.insert(pathToObj); |
1962 | 0 | } |
1963 | | // Object libs should not be propagated transitively. It's especially |
1964 | | // important for LinkObjRHSObject2 test where the absence of the propagation |
1965 | | // is tested. |
1966 | 0 | for (auto const& linkedTarget : |
1967 | 0 | target.Target->GetLinkImplementationEntries()) { |
1968 | 0 | auto objAlias = linkedTarget.Value + FASTBUILD_OBJECTS_ALIAS_POSTFIX; |
1969 | 0 | LogMessage("Object target is linked transitively " + objAlias); |
1970 | 0 | linkedObjects.emplace(std::move(objAlias)); |
1971 | 0 | } |
1972 | 0 | } |
1973 | | |
1974 | | void cmFastbuildNormalTargetGenerator::AppendCommandLineDep( |
1975 | | FastbuildLinkerNode& linkerNode, |
1976 | | cmComputeLinkInformation::Item const& item) const |
1977 | 0 | { |
1978 | 0 | LogMessage("AppendCommandLineDep(...)"); |
1979 | | // Tested in: |
1980 | | // "LinkDirectory" (TargetType::EXECUTABLE), |
1981 | | // "ObjC.simple-build-test" (TargetType::SHARED_LIBRARY), |
1982 | | // "XCTest" (TargetType::MODULE_LIBRARY) tests. |
1983 | |
|
1984 | 0 | std::string formatted; |
1985 | 0 | if (item.Target && item.Target->IsImported()) { |
1986 | 0 | formatted = GetImportedLoc(item); |
1987 | 0 | } else { |
1988 | 0 | formatted = item.GetFormattedItem(item.Value.Value).Value; |
1989 | 0 | } |
1990 | 0 | formatted = this->ConvertToFastbuildPath(formatted); |
1991 | |
|
1992 | 0 | LogMessage( |
1993 | 0 | cmStrCat("Unknown link dep: ", formatted, ", adding to command line")); |
1994 | | |
1995 | | // Only add real artifacts to .Libraries2, otherwise Fastbuild will always |
1996 | | // consider the target out-of-date (since its input doesn't exist). |
1997 | 0 | if (item.IsPath == cmComputeLinkInformation::ItemIsPath::Yes && |
1998 | 0 | item.GetFeatureName() == "DEFAULT") { |
1999 | 0 | linkerNode.LinkerOptions += |
2000 | 0 | (" " + cmGlobalFastbuildGenerator::QuoteIfHasSpaces(formatted)); |
2001 | 0 | AppendToLibraries2IfApplicable(linkerNode, std::move(formatted)); |
2002 | 0 | } else { |
2003 | | // It's some link option, not a path. |
2004 | 0 | linkerNode.LinkerOptions += (" " + formatted); |
2005 | 0 | } |
2006 | 0 | } |
2007 | | |
2008 | | void cmFastbuildNormalTargetGenerator::AppendToLibraries2IfApplicable( |
2009 | | FastbuildLinkerNode& linkerNode, std::string dep) const |
2010 | 0 | { |
2011 | | // Strings like "-framework Cocoa" in .Libraries2 node will always make the |
2012 | | // target out-of-date (since it never exists). |
2013 | 0 | if (this->GeneratorTarget->IsApple() && |
2014 | 0 | cmSystemTools::StringStartsWith(dep, "-framework")) { |
2015 | 0 | LogMessage(cmStrCat("Not adding framework: ", dep, " to .Libraries2")); |
2016 | 0 | return; |
2017 | 0 | } |
2018 | | |
2019 | 0 | auto const target = this->GetGlobalGenerator()->GetTargetByOutputName(dep); |
2020 | | // Fastbuild doesn't support executables in .Libraries2, though we can use |
2021 | | // Executables via "-bundle_loader" on Apple. |
2022 | 0 | if (this->GeneratorTarget->IsApple() && target && |
2023 | 0 | !target->LinkerNode.empty() && |
2024 | 0 | target->LinkerNode[0].Type == FastbuildLinkerNode::EXECUTABLE) { |
2025 | 0 | LogMessage(cmStrCat("Not adding DLL/Executable(", linkerNode.Name, |
2026 | 0 | " to .Libraries2")); |
2027 | 0 | return; |
2028 | 0 | } |
2029 | | |
2030 | | // Adding to .Libraries2 for tracking. |
2031 | 0 | LogMessage(cmStrCat("Adding ", dep, " .Libraries2")); |
2032 | 0 | linkerNode.Libraries2.emplace_back(std::move(dep)); |
2033 | 0 | } |
2034 | | |
2035 | | void cmFastbuildNormalTargetGenerator::AppendLINK_DEPENDS( |
2036 | | FastbuildLinkerNode& linkerNode) const |
2037 | 0 | { |
2038 | | // LINK_DEPENDS and such. |
2039 | | // Tested in "BuildDepends" test. |
2040 | 0 | for (std::string const& lang : Languages) { |
2041 | 0 | for (BT<std::string> const& dep : |
2042 | 0 | this->GeneratorTarget->GetLinkDepends(Config, lang)) { |
2043 | | // We can't add "LINK_DEPENDS" to .PreBuildDependencies, since FASTBuild |
2044 | | // only forces such targets to be built and doesn't force re-linking if |
2045 | | // they've changed. |
2046 | 0 | linkerNode.Libraries2.emplace_back( |
2047 | 0 | this->ConvertToFastbuildPath(dep.Value)); |
2048 | 0 | } |
2049 | 0 | } |
2050 | 0 | } |
2051 | | |
2052 | | void cmFastbuildNormalTargetGenerator::AppendLinkDep( |
2053 | | FastbuildLinkerNode& linkerNode, std::string dep) const |
2054 | 0 | { |
2055 | 0 | LogMessage(cmStrCat("AppendLinkDep: ", dep, |
2056 | 0 | " to .LibrarianAdditionalInputs/.Libraries")); |
2057 | 0 | linkerNode.LibrarianAdditionalInputs.emplace_back(std::move(dep)); |
2058 | 0 | } |
2059 | | |
2060 | | void cmFastbuildNormalTargetGenerator::AppendDirectObjectLibs( |
2061 | | FastbuildLinkerNode& linkerNode, std::set<std::string>& linkedObjects) |
2062 | 0 | { |
2063 | 0 | auto const srcs = this->GeneratorTarget->GetSourceFiles(Config); |
2064 | 0 | for (auto const& entry : srcs) { |
2065 | 0 | auto const objLib = entry.Value->GetObjectLibrary(); |
2066 | 0 | auto const objPath = entry.Value->GetFullPath(); |
2067 | 0 | LogMessage("Source obj entry: " + objPath); |
2068 | 0 | if (!objLib.empty()) { |
2069 | 0 | auto* const objTarget = |
2070 | 0 | this->LocalGenerator->FindGeneratorTargetToUse(objLib); |
2071 | 0 | if (objTarget) { |
2072 | 0 | LogMessage("Imported: " + std::to_string(objTarget->IsImported())); |
2073 | 0 | std::string fastbuildTarget; |
2074 | | // If target is imported - we don't have it in our build file, so can't |
2075 | | // refer to it by name. Use file path to the object then. |
2076 | | // Tested in "ExportImport" test. |
2077 | 0 | if (objTarget->IsImported()) { |
2078 | 0 | fastbuildTarget = entry.Value->GetFullPath(); |
2079 | 0 | } else { |
2080 | | // Mark all target objects as linked. |
2081 | 0 | linkedObjects.emplace(this->ConvertToFastbuildPath(objPath)); |
2082 | 0 | fastbuildTarget = |
2083 | 0 | objTarget->GetName() + FASTBUILD_OBJECTS_ALIAS_POSTFIX; |
2084 | 0 | } |
2085 | 0 | if (linkedObjects.emplace(fastbuildTarget).second) { |
2086 | 0 | LogMessage("Adding object target: " + fastbuildTarget); |
2087 | 0 | linkerNode.LibrarianAdditionalInputs.emplace_back( |
2088 | 0 | std::move(fastbuildTarget)); |
2089 | 0 | } |
2090 | 0 | } |
2091 | 0 | } |
2092 | 0 | } |
2093 | 0 | } |
2094 | | |
2095 | | void cmFastbuildNormalTargetGenerator::AppendLinkDeps( |
2096 | | std::set<FastbuildTargetDep>& preBuildDeps, FastbuildLinkerNode& linkerNode, |
2097 | | FastbuildLinkerNode& cudaDeviceLinkLinkerNode) |
2098 | 0 | { |
2099 | 0 | std::set<std::string> linkedObjects; |
2100 | 0 | cmComputeLinkInformation const* linkInfo = |
2101 | 0 | this->GeneratorTarget->GetLinkInformation(Config); |
2102 | 0 | if (!linkInfo) { |
2103 | 0 | return; |
2104 | 0 | } |
2105 | | |
2106 | 0 | UsingCommandLine = false; |
2107 | 0 | AppendLINK_DEPENDS(linkerNode); |
2108 | | // Object libs that are linked directly to target (e.g. |
2109 | | // add_executable(test_exe archiveObjs) |
2110 | 0 | AppendDirectObjectLibs(linkerNode, linkedObjects); |
2111 | 0 | std::size_t numberOfDirectlyLinkedObjects = |
2112 | 0 | linkerNode.LibrarianAdditionalInputs.size(); |
2113 | | // target_link_libraries. |
2114 | 0 | cmComputeLinkInformation::ItemVector const items = linkInfo->GetItems(); |
2115 | |
|
2116 | 0 | LogMessage(cmStrCat("Link items size: ", items.size())); |
2117 | 0 | for (cmComputeLinkInformation::Item const& item : items) { |
2118 | 0 | std::string const feature = item.GetFeatureName(); |
2119 | 0 | LogMessage("GetFeatureName: " + feature); |
2120 | 0 | std::string const formatted = |
2121 | 0 | item.GetFormattedItem(item.Value.Value).Value; |
2122 | 0 | if (!feature.empty()) { |
2123 | 0 | LogMessage("GetFormattedItem: " + |
2124 | 0 | item.GetFormattedItem(item.Value.Value).Value); |
2125 | 0 | } |
2126 | | // We're linked to `$<TARGET_OBJECTS>`. |
2127 | | // Static libs transitively propagate such deps, see: |
2128 | | // https://cmake.org/cmake/help/latest/command/target_link_libraries.html#linking-object-libraries-via-target-objects |
2129 | 0 | if (item.ObjectSource && |
2130 | 0 | linkerNode.Type != FastbuildLinkerNode::STATIC_LIBRARY) { |
2131 | | // Tested in "ObjectLibrary" test. |
2132 | 0 | std::string const libName = item.ObjectSource->GetObjectLibrary(); |
2133 | 0 | std::string dep = libName + FASTBUILD_OBJECTS_ALIAS_POSTFIX; |
2134 | 0 | if (linkedObjects.emplace(dep).second) { |
2135 | 0 | FastbuildTargetDep targetDep{ libName }; |
2136 | 0 | targetDep.Type = FastbuildTargetDepType::ORDER_ONLY; |
2137 | 0 | preBuildDeps.emplace(std::move(targetDep)); |
2138 | |
|
2139 | 0 | cmTarget const* importedTarget = |
2140 | 0 | this->LocalGenerator->GetMakefile()->FindImportedTarget(libName); |
2141 | | // Add direct path to the object for imported target |
2142 | | // since such targets are not defined in fbuild.bff file. |
2143 | 0 | if (importedTarget) { |
2144 | 0 | LogMessage( |
2145 | 0 | cmStrCat("Adding ", formatted, " to LibrarianAdditionalInputs")); |
2146 | 0 | linkerNode.LibrarianAdditionalInputs.emplace_back(formatted); |
2147 | 0 | } else { |
2148 | 0 | LogMessage( |
2149 | 0 | cmStrCat("Adding ", dep, " to LibrarianAdditionalInputs")); |
2150 | 0 | linkerNode.LibrarianAdditionalInputs.emplace_back(std::move(dep)); |
2151 | 0 | } |
2152 | 0 | } |
2153 | 0 | } else if (linkerNode.Type == FastbuildLinkerNode::STATIC_LIBRARY) { |
2154 | 0 | LogMessage(cmStrCat("Skipping linking to STATIC_LIBRARY (", |
2155 | 0 | linkerNode.Name, ')')); |
2156 | 0 | continue; |
2157 | 0 | } |
2158 | | // We're linked to exact target. |
2159 | 0 | else if (item.Target) { |
2160 | 0 | AppendTargetDep(linkerNode, linkedObjects, item); |
2161 | 0 | AppendPrebuildDeps(linkerNode, item); |
2162 | 0 | if (!item.Target->IsImported() && |
2163 | 0 | item.Target->GetType() == cmStateEnums::OBJECT_LIBRARY) { |
2164 | 0 | ++numberOfDirectlyLinkedObjects; |
2165 | 0 | cudaDeviceLinkLinkerNode.LibrarianAdditionalInputs.emplace_back( |
2166 | 0 | cmStrCat(item.Target->GetName(), FASTBUILD_OBJECTS_ALIAS_POSTFIX)); |
2167 | 0 | } |
2168 | |
|
2169 | 0 | } else { |
2170 | 0 | AppendCommandLineDep(linkerNode, item); |
2171 | 0 | UsingCommandLine = true; |
2172 | 0 | } |
2173 | 0 | } |
2174 | 0 | AppendExternalObject(linkerNode, linkedObjects); |
2175 | |
|
2176 | 0 | if (!cudaDeviceLinkLinkerNode.Name.empty()) { |
2177 | 0 | linkerNode.LibrarianAdditionalInputs.push_back( |
2178 | 0 | cudaDeviceLinkLinkerNode.Name); |
2179 | | // CUDA device-link stub needs to go AFTER direct object dependencies, but |
2180 | | // BEFORE all other dependencies. Needed for the correct left-to-right |
2181 | | // symbols resolution on Linux. |
2182 | 0 | std::swap( |
2183 | 0 | linkerNode.LibrarianAdditionalInputs[numberOfDirectlyLinkedObjects], |
2184 | 0 | linkerNode.LibrarianAdditionalInputs.back()); |
2185 | 0 | } |
2186 | 0 | } |
2187 | | |
2188 | | void cmFastbuildNormalTargetGenerator::AddLipoCommand(FastbuildTarget& target) |
2189 | 0 | { |
2190 | 0 | static auto const lipo = cmSystemTools::FindProgram("lipo"); |
2191 | 0 | LogMessage("found lipo at " + lipo); |
2192 | 0 | FastbuildExecNode exec; |
2193 | 0 | exec.ExecExecutable = lipo; |
2194 | 0 | exec.ExecOutput = target.RealOutput; |
2195 | 0 | if (exec.ExecOutput != target.Name) { |
2196 | 0 | exec.Name = target.Name; |
2197 | 0 | } |
2198 | 0 | for (auto const& ArchSpecificTarget : target.LinkerNode) { |
2199 | 0 | exec.ExecInput.emplace_back(ArchSpecificTarget.LinkerOutput); |
2200 | 0 | } |
2201 | 0 | exec.ExecArguments += cmStrCat("-create -output ", target.RealOutput, " ", |
2202 | 0 | cmJoin(exec.ExecInput, " ")); |
2203 | 0 | target.PostBuildExecNodes.Alias.PreBuildDependencies.emplace( |
2204 | 0 | exec.ExecOutput); |
2205 | 0 | target.PostBuildExecNodes.Nodes.emplace_back(std::move(exec)); |
2206 | 0 | } |
2207 | | |
2208 | | void cmFastbuildNormalTargetGenerator::GenerateLink( |
2209 | | FastbuildTarget& target, std::vector<std::string> const& objectDepends) |
2210 | 0 | { |
2211 | 0 | std::string const targetName = this->GetTargetName(); |
2212 | 0 | cmGeneratorTarget::Names const targetNames = DetectOutput(); |
2213 | 0 | LogMessage("targetNames.Real: " + targetNames.Real); |
2214 | 0 | LogMessage("targetNames.ImportOutput: " + targetNames.ImportOutput); |
2215 | 0 | LogMessage("targetNames.SharedObject: " + targetNames.SharedObject); |
2216 | 0 | LogMessage("targetNames.Base: " + targetNames.Base); |
2217 | |
|
2218 | 0 | std::vector<std::string> allNodes; |
2219 | 0 | auto const arches = this->GetArches(); |
2220 | 0 | for (std::size_t i = 0; i < arches.size(); ++i) { |
2221 | 0 | auto const& arch = arches[i]; |
2222 | 0 | FastbuildLinkerNode linkerNode; |
2223 | 0 | ProcessManifests(linkerNode); |
2224 | | // Objects built by the current target. |
2225 | 0 | for (auto const& objectList : target.ObjectListNodes) { |
2226 | 0 | if (objectList.arch.empty() || objectList.arch == arch) { |
2227 | 0 | linkerNode.LibrarianAdditionalInputs.push_back(objectList.Name); |
2228 | 0 | } |
2229 | 0 | } |
2230 | | |
2231 | | // Detection of the link command as follows: |
2232 | 0 | auto const type = this->GeneratorTarget->GetType(); |
2233 | 0 | switch (type) { |
2234 | 0 | case cmStateEnums::EXECUTABLE: { |
2235 | 0 | LogMessage("Generating EXECUTABLE"); |
2236 | 0 | linkerNode.Type = FastbuildLinkerNode::EXECUTABLE; |
2237 | 0 | break; |
2238 | 0 | } |
2239 | 0 | case cmStateEnums::MODULE_LIBRARY: { |
2240 | 0 | LogMessage("Generating MODULE_LIBRARY"); |
2241 | 0 | linkerNode.Type = FastbuildLinkerNode::SHARED_LIBRARY; |
2242 | 0 | break; |
2243 | 0 | } |
2244 | 0 | case cmStateEnums::SHARED_LIBRARY: { |
2245 | 0 | LogMessage("Generating SHARED_LIBRARY"); |
2246 | 0 | linkerNode.Type = FastbuildLinkerNode::SHARED_LIBRARY; |
2247 | 0 | break; |
2248 | 0 | } |
2249 | 0 | case cmStateEnums::STATIC_LIBRARY: { |
2250 | 0 | LogMessage("Generating STATIC_LIBRARY"); |
2251 | 0 | linkerNode.Type = FastbuildLinkerNode::STATIC_LIBRARY; |
2252 | 0 | break; |
2253 | 0 | } |
2254 | 0 | case cmStateEnums::OBJECT_LIBRARY: { |
2255 | 0 | LogMessage("Generating OBJECT_LIBRARY"); |
2256 | 0 | return; |
2257 | 0 | } |
2258 | 0 | default: { |
2259 | 0 | LogMessage("Skipping GenerateLink"); |
2260 | 0 | return; |
2261 | 0 | } |
2262 | 0 | } |
2263 | | |
2264 | 0 | std::string const targetOutput = |
2265 | 0 | ConvertToFastbuildPath(GeneratorTarget->GetFullPath(Config)); |
2266 | 0 | std::string targetOutputReal = ConvertToFastbuildPath( |
2267 | 0 | GeneratorTarget->GetFullPath(Config, cmStateEnums::RuntimeBinaryArtifact, |
2268 | 0 | /*realname=*/true)); |
2269 | 0 | LogMessage("targetOutput: " + targetOutput); |
2270 | 0 | LogMessage("targetOutputReal: " + targetOutputReal); |
2271 | |
|
2272 | 0 | std::string const output = |
2273 | 0 | cmSystemTools::GetFilenameName(targetNames.Output); |
2274 | 0 | std::string const outputReal = |
2275 | 0 | cmSystemTools::GetFilenameName(targetNames.Real); |
2276 | | // Generate "Copy" nodes for copying Framework / Bundle resources. |
2277 | 0 | AppendExtraResources(linkerNode.PreBuildDependencies); |
2278 | |
|
2279 | 0 | if (type == cmStateEnums::EXECUTABLE || |
2280 | 0 | type == cmStateEnums::SHARED_LIBRARY) { |
2281 | | // Tested in "RunCMake.BuildDepends" test (we need to rebuild when |
2282 | | // manifest changes). |
2283 | 0 | std::copy(objectDepends.begin(), objectDepends.end(), |
2284 | 0 | std::back_inserter(linkerNode.Libraries2)); |
2285 | 0 | } |
2286 | |
|
2287 | 0 | if (GeneratorTarget->IsAppBundleOnApple()) { |
2288 | | // Create the app bundle |
2289 | 0 | std::string outpath = GeneratorTarget->GetDirectory(Config); |
2290 | 0 | this->OSXBundleGenerator->CreateAppBundle(targetNames.Output, outpath, |
2291 | 0 | Config); |
2292 | 0 | targetOutputReal = cmStrCat(outpath, '/', outputReal); |
2293 | 0 | targetOutputReal = this->ConvertToFastbuildPath(targetOutputReal); |
2294 | 0 | } else if (GeneratorTarget->IsFrameworkOnApple()) { |
2295 | | // Create the library framework. |
2296 | 0 | this->OSXBundleGenerator->CreateFramework( |
2297 | 0 | targetNames.Output, GeneratorTarget->GetDirectory(Config), Config); |
2298 | 0 | } else if (GeneratorTarget->IsCFBundleOnApple()) { |
2299 | | // Create the core foundation bundle. |
2300 | 0 | this->OSXBundleGenerator->CreateCFBundle( |
2301 | 0 | targetNames.Output, GeneratorTarget->GetDirectory(Config), Config); |
2302 | 0 | } |
2303 | |
|
2304 | 0 | std::string linkCmd; |
2305 | 0 | if (!DetectBaseLinkerCommand(linkCmd, arch, targetNames)) { |
2306 | 0 | LogMessage("No linker command detected"); |
2307 | 0 | return; |
2308 | 0 | } |
2309 | | |
2310 | 0 | std::string executable; |
2311 | 0 | std::string linkerOptions; |
2312 | 0 | std::string linkerType = "auto"; |
2313 | |
|
2314 | 0 | GetLinkerExecutableAndArgs(linkCmd, executable, linkerOptions); |
2315 | |
|
2316 | 0 | linkerNode.Compiler = ".Compiler_dummy"; |
2317 | 0 | linkerNode.CompilerOptions = " "; |
2318 | |
|
2319 | 0 | linkerNode.Name = targetName; |
2320 | 0 | linkerNode.LinkerOutput = targetOutputReal; |
2321 | 0 | this->GetGlobalGenerator()->AddFileToClean(linkerNode.LinkerOutput); |
2322 | 0 | target.RealOutput = targetOutputReal; |
2323 | 0 | if (!arch.empty()) { |
2324 | 0 | linkerNode.Name += cmStrCat('-', arch); |
2325 | 0 | linkerNode.LinkerOutput += cmStrCat('.', arch); |
2326 | 0 | linkerNode.Arch = arch; |
2327 | 0 | } |
2328 | 0 | linkerNode.Linker = executable; |
2329 | 0 | linkerNode.LinkerType = linkerType; |
2330 | 0 | linkerNode.LinkerOptions += linkerOptions; |
2331 | | |
2332 | | // Check if we have CUDA device link stub for this target. |
2333 | 0 | FastbuildLinkerNode dummyCudaDeviceLinkNode; |
2334 | 0 | AppendLinkDeps(target.PreBuildDependencies, linkerNode, |
2335 | 0 | target.CudaDeviceLinkNode.size() > i |
2336 | 0 | ? target.CudaDeviceLinkNode[i] |
2337 | 0 | : dummyCudaDeviceLinkNode); |
2338 | 0 | ApplyLWYUToLinkerCommand(linkerNode); |
2339 | | |
2340 | | // On macOS, only the last LinkerNode performs lipo in POST_BUILD. |
2341 | | // Make it depend on all previous nodes to ensure correct execution order. |
2342 | 0 | if (i == arches.size() - 1) { |
2343 | 0 | for (auto& prevNode : allNodes) { |
2344 | 0 | linkerNode.PreBuildDependencies.emplace(std::move(prevNode)); |
2345 | 0 | } |
2346 | 0 | } else { |
2347 | 0 | allNodes.emplace_back(linkerNode.Name); |
2348 | 0 | } |
2349 | 0 | if (!target.ObjectListNodes.empty()) { |
2350 | | // Just reuse any of compiler options mainly for the correct IDE project |
2351 | | // generation. |
2352 | 0 | linkerNode.CompilerOptions = target.ObjectListNodes[0].CompilerOptions; |
2353 | 0 | } |
2354 | 0 | target.LinkerNode.emplace_back(std::move(linkerNode)); |
2355 | 0 | } |
2356 | 0 | } |
2357 | | |
2358 | | std::vector<FastbuildExecNode> |
2359 | | cmFastbuildNormalTargetGenerator::GetSymlinkExecs() const |
2360 | 0 | { |
2361 | 0 | std::vector<FastbuildExecNode> res; |
2362 | 0 | cmGeneratorTarget::Names const targetNames = DetectOutput(); |
2363 | 0 | LogMessage("targetNames.Real: " + targetNames.Real); |
2364 | 0 | LogMessage("targetNames.ImportOutput: " + targetNames.ImportOutput); |
2365 | 0 | LogMessage("targetNames.SharedObject: " + targetNames.SharedObject); |
2366 | 0 | LogMessage("targetNames.Base: " + targetNames.Base); |
2367 | |
|
2368 | 0 | std::string const targetOutput = |
2369 | 0 | ConvertToFastbuildPath(GeneratorTarget->GetFullPath(Config)); |
2370 | 0 | std::string const targetOutputReal = ConvertToFastbuildPath( |
2371 | 0 | GeneratorTarget->GetFullPath(Config, cmStateEnums::RuntimeBinaryArtifact, |
2372 | 0 | /*realname=*/true)); |
2373 | 0 | LogMessage("targetOutput: " + targetOutput); |
2374 | |
|
2375 | 0 | LogMessage("targetOutputReal: " + targetOutputReal); |
2376 | |
|
2377 | 0 | if (targetOutput != targetOutputReal && |
2378 | 0 | !GeneratorTarget->IsFrameworkOnApple()) { |
2379 | 0 | auto const generateSymlinkCommand = [&](std::string const& from, |
2380 | 0 | std::string const& to) { |
2381 | 0 | if (from.empty() || to.empty() || from == to) { |
2382 | 0 | return; |
2383 | 0 | } |
2384 | 0 | LogMessage(cmStrCat("Symlinking ", from, " -> ", to)); |
2385 | 0 | FastbuildExecNode postBuildExecNode; |
2386 | 0 | postBuildExecNode.Name = "cmake_symlink_" + to; |
2387 | 0 | postBuildExecNode.ExecOutput = |
2388 | 0 | cmJoin({ GeneratorTarget->GetDirectory(Config), to }, "/"); |
2389 | 0 | postBuildExecNode.ExecExecutable = cmSystemTools::GetCMakeCommand(); |
2390 | 0 | postBuildExecNode.ExecArguments = cmStrCat( |
2391 | 0 | "-E cmake_symlink_executable ", |
2392 | 0 | cmGlobalFastbuildGenerator::QuoteIfHasSpaces(from), ' ', |
2393 | 0 | cmGlobalFastbuildGenerator::QuoteIfHasSpaces( |
2394 | 0 | this->ConvertToFastbuildPath(postBuildExecNode.ExecOutput))); |
2395 | 0 | res.emplace_back(std::move(postBuildExecNode)); |
2396 | 0 | }; |
2397 | 0 | generateSymlinkCommand(targetNames.Real, targetNames.Output); |
2398 | 0 | generateSymlinkCommand(targetNames.Real, targetNames.SharedObject); |
2399 | 0 | generateSymlinkCommand(targetNames.ImportReal, targetNames.ImportOutput); |
2400 | 0 | } |
2401 | 0 | return res; |
2402 | 0 | } |