The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1984 lines
99KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2020 - Raw Material Software Limited
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 6 End-User License
  8. Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
  9. End User License Agreement: www.juce.com/juce-6-licence
  10. Privacy Policy: www.juce.com/juce-privacy-policy
  11. Or: You may also use this code under the terms of the GPL v3 (see
  12. www.gnu.org/licenses).
  13. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  14. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  15. DISCLAIMED.
  16. ==============================================================================
  17. */
  18. #pragma once
  19. inline String msBuildEscape (String str)
  20. {
  21. // see https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild-special-characters?view=vs-2019
  22. for (const auto& special : { "%", "$", "@", "'", ";", "?", "\""})
  23. str = str.replace (special, "%" + String::toHexString (*special));
  24. return str;
  25. }
  26. //==============================================================================
  27. class MSVCProjectExporterBase : public ProjectExporter
  28. {
  29. public:
  30. MSVCProjectExporterBase (Project& p, const ValueTree& t, String folderName)
  31. : ProjectExporter (p, t),
  32. IPPLibraryValue (settings, Ids::IPPLibrary, getUndoManager()),
  33. IPP1ALibraryValue (settings, Ids::IPP1ALibrary, getUndoManager()),
  34. MKL1ALibraryValue (settings, Ids::MKL1ALibrary, getUndoManager()),
  35. platformToolsetValue (settings, Ids::toolset, getUndoManager()),
  36. targetPlatformVersion (settings, Ids::windowsTargetPlatformVersion, getUndoManager()),
  37. manifestFileValue (settings, Ids::msvcManifestFile, getUndoManager())
  38. {
  39. targetLocationValue.setDefault (getDefaultBuildsRootFolder() + folderName);
  40. }
  41. virtual int getVisualStudioVersion() const = 0;
  42. virtual String getSolutionComment() const = 0;
  43. virtual String getToolsVersion() const = 0;
  44. virtual String getDefaultToolset() const = 0;
  45. virtual String getDefaultWindowsTargetPlatformVersion() const = 0;
  46. //==============================================================================
  47. String getIPPLibrary() const { return IPPLibraryValue.get(); }
  48. String getIPP1ALibrary() const { return IPP1ALibraryValue.get(); }
  49. String getMKL1ALibrary() const { return MKL1ALibraryValue.get(); }
  50. String getPlatformToolset() const { return platformToolsetValue.get(); }
  51. String getWindowsTargetPlatformVersion() const { return targetPlatformVersion.get(); }
  52. //==============================================================================
  53. void addToolsetProperty (PropertyListBuilder& props, const char** names, const var* values, int num)
  54. {
  55. props.add (new ChoicePropertyComponent (platformToolsetValue, "Platform Toolset",
  56. StringArray (names, num), { values, num }),
  57. "Specifies the version of the platform toolset that will be used when building this project.");
  58. }
  59. void create (const OwnedArray<LibraryModule>&) const override
  60. {
  61. createResourcesAndIcon();
  62. createPackagesConfigFile();
  63. for (int i = 0; i < targets.size(); ++i)
  64. if (auto* target = targets[i])
  65. target->writeProjectFile();
  66. build_tools::writeStreamToFile (getSLNFile(), [&] (MemoryOutputStream& mo)
  67. {
  68. writeSolutionFile (mo, "11.00", getSolutionComment());
  69. });
  70. }
  71. //==============================================================================
  72. void updateDeprecatedSettings() override
  73. {
  74. {
  75. auto oldStylePrebuildCommand = getSettingString (Ids::prebuildCommand);
  76. settings.removeProperty (Ids::prebuildCommand, nullptr);
  77. if (oldStylePrebuildCommand.isNotEmpty())
  78. for (ConfigIterator config (*this); config.next();)
  79. dynamic_cast<MSVCBuildConfiguration&> (*config).getValue (Ids::prebuildCommand) = oldStylePrebuildCommand;
  80. }
  81. {
  82. auto oldStyleLibName = getSettingString ("libraryName_Debug");
  83. settings.removeProperty ("libraryName_Debug", nullptr);
  84. if (oldStyleLibName.isNotEmpty())
  85. for (ConfigIterator config (*this); config.next();)
  86. if (config->isDebug())
  87. config->getValue (Ids::targetName) = oldStyleLibName;
  88. }
  89. {
  90. auto oldStyleLibName = getSettingString ("libraryName_Release");
  91. settings.removeProperty ("libraryName_Release", nullptr);
  92. if (oldStyleLibName.isNotEmpty())
  93. for (ConfigIterator config (*this); config.next();)
  94. if (! config->isDebug())
  95. config->getValue (Ids::targetName) = oldStyleLibName;
  96. }
  97. for (ConfigIterator i (*this); i.next();)
  98. dynamic_cast<MSVCBuildConfiguration&> (*i).updateOldLTOSetting();
  99. }
  100. void initialiseDependencyPathValues() override
  101. {
  102. vstLegacyPathValueWrapper.init ({ settings, Ids::vstLegacyFolder, nullptr },
  103. getAppSettings().getStoredPath (Ids::vstLegacyPath, TargetOS::windows), TargetOS::windows);
  104. aaxPathValueWrapper.init ({ settings, Ids::aaxFolder, nullptr },
  105. getAppSettings().getStoredPath (Ids::aaxPath, TargetOS::windows), TargetOS::windows);
  106. rtasPathValueWrapper.init ({ settings, Ids::rtasFolder, nullptr },
  107. getAppSettings().getStoredPath (Ids::rtasPath, TargetOS::windows), TargetOS::windows);
  108. }
  109. //==============================================================================
  110. class MSVCBuildConfiguration : public BuildConfiguration,
  111. private Value::Listener
  112. {
  113. public:
  114. MSVCBuildConfiguration (Project& p, const ValueTree& settings, const ProjectExporter& e)
  115. : BuildConfiguration (p, settings, e),
  116. warningLevelValue (config, Ids::winWarningLevel, getUndoManager(), 4),
  117. warningsAreErrorsValue (config, Ids::warningsAreErrors, getUndoManager(), false),
  118. prebuildCommandValue (config, Ids::prebuildCommand, getUndoManager()),
  119. postbuildCommandValue (config, Ids::postbuildCommand, getUndoManager()),
  120. generateDebugSymbolsValue (config, Ids::alwaysGenerateDebugSymbols, getUndoManager(), false),
  121. generateManifestValue (config, Ids::generateManifest, getUndoManager(), true),
  122. enableIncrementalLinkingValue (config, Ids::enableIncrementalLinking, getUndoManager(), false),
  123. useRuntimeLibDLLValue (config, Ids::useRuntimeLibDLL, getUndoManager(), true),
  124. multiProcessorCompilationValue (config, Ids::multiProcessorCompilation, getUndoManager(), true),
  125. intermediatesPathValue (config, Ids::intermediatesPath, getUndoManager()),
  126. characterSetValue (config, Ids::characterSet, getUndoManager()),
  127. architectureTypeValue (config, Ids::winArchitecture, getUndoManager(), get64BitArchName()),
  128. fastMathValue (config, Ids::fastMath, getUndoManager()),
  129. debugInformationFormatValue (config, Ids::debugInformationFormat, getUndoManager(), isDebug() ? "ProgramDatabase" : "None"),
  130. pluginBinaryCopyStepValue (config, Ids::enablePluginBinaryCopyStep, getUndoManager(), false),
  131. vstBinaryLocation (config, Ids::vstBinaryLocation, getUndoManager()),
  132. vst3BinaryLocation (config, Ids::vst3BinaryLocation, getUndoManager()),
  133. rtasBinaryLocation (config, Ids::rtasBinaryLocation, getUndoManager()),
  134. aaxBinaryLocation (config, Ids::aaxBinaryLocation, getUndoManager()),
  135. unityPluginBinaryLocation (config, Ids::unityPluginBinaryLocation, getUndoManager(), {})
  136. {
  137. setPluginBinaryCopyLocationDefaults();
  138. optimisationLevelValue.setDefault (isDebug() ? optimisationOff : optimiseFull);
  139. architectureValueToListenTo = architectureTypeValue.getPropertyAsValue();
  140. architectureValueToListenTo.addListener (this);
  141. }
  142. //==============================================================================
  143. int getWarningLevel() const { return warningLevelValue.get(); }
  144. bool areWarningsTreatedAsErrors() const { return warningsAreErrorsValue.get(); }
  145. String getPrebuildCommandString() const { return prebuildCommandValue.get(); }
  146. String getPostbuildCommandString() const { return postbuildCommandValue.get(); }
  147. String getVSTBinaryLocationString() const { return vstBinaryLocation.get(); }
  148. String getVST3BinaryLocationString() const { return vst3BinaryLocation.get(); }
  149. String getRTASBinaryLocationString() const { return rtasBinaryLocation.get();}
  150. String getAAXBinaryLocationString() const { return aaxBinaryLocation.get();}
  151. String getUnityPluginBinaryLocationString() const { return unityPluginBinaryLocation.get(); }
  152. String getIntermediatesPathString() const { return intermediatesPathValue.get(); }
  153. String getCharacterSetString() const { return characterSetValue.get(); }
  154. String get64BitArchName() const { return "x64"; }
  155. String get32BitArchName() const { return "Win32"; }
  156. String getArchitectureString() const { return architectureTypeValue.get(); }
  157. String getDebugInformationFormatString() const { return debugInformationFormatValue.get(); }
  158. bool shouldGenerateDebugSymbols() const { return generateDebugSymbolsValue.get(); }
  159. bool shouldGenerateManifest() const { return generateManifestValue.get(); }
  160. bool shouldLinkIncremental() const { return enableIncrementalLinkingValue.get(); }
  161. bool isUsingRuntimeLibDLL() const { return useRuntimeLibDLLValue.get(); }
  162. bool shouldUseMultiProcessorCompilation() const { return multiProcessorCompilationValue.get(); }
  163. bool is64Bit() const { return getArchitectureString() == get64BitArchName(); }
  164. bool isFastMathEnabled() const { return fastMathValue.get(); }
  165. bool isPluginBinaryCopyStepEnabled() const { return pluginBinaryCopyStepValue.get(); }
  166. //==============================================================================
  167. String createMSVCConfigName() const
  168. {
  169. return getName() + "|" + (is64Bit() ? "x64" : "Win32");
  170. }
  171. String getOutputFilename (const String& suffix, bool forceSuffix, bool forceUnityPrefix) const
  172. {
  173. auto target = File::createLegalFileName (getTargetBinaryNameString (forceUnityPrefix).trim());
  174. if (forceSuffix || ! target.containsChar ('.'))
  175. return target.upToLastOccurrenceOf (".", false, false) + suffix;
  176. return target;
  177. }
  178. void createConfigProperties (PropertyListBuilder& props) override
  179. {
  180. if (project.isAudioPluginProject())
  181. addVisualStudioPluginInstallPathProperties (props);
  182. props.add (new ChoicePropertyComponent (architectureTypeValue, "Architecture",
  183. { get32BitArchName(), get64BitArchName() },
  184. { get32BitArchName(), get64BitArchName() }),
  185. "Whether to use a 32-bit or 64-bit architecture.");
  186. props.add (new ChoicePropertyComponentWithEnablement (debugInformationFormatValue,
  187. isDebug() ? isDebugValue : generateDebugSymbolsValue,
  188. "Debug Information Format",
  189. { "None", "C7 Compatible (/Z7)", "Program Database (/Zi)", "Program Database for Edit And Continue (/ZI)" },
  190. { "None", "OldStyle", "ProgramDatabase", "EditAndContinue" }),
  191. "The type of debugging information created for your program for this configuration."
  192. " This will always be used in a debug configuration and will be used in a release configuration"
  193. " with forced generation of debug symbols.");
  194. props.add (new ChoicePropertyComponent (fastMathValue, "Relax IEEE Compliance"),
  195. "Enable this to use FAST_MATH non-IEEE mode. (Warning: this can have unexpected results!)");
  196. props.add (new ChoicePropertyComponent (optimisationLevelValue, "Optimisation",
  197. { "Disabled (/Od)", "Minimise size (/O1)", "Maximise speed (/O2)", "Full optimisation (/Ox)" },
  198. { optimisationOff, optimiseMinSize, optimiseMaxSpeed, optimiseFull }),
  199. "The optimisation level for this configuration");
  200. props.add (new TextPropertyComponent (intermediatesPathValue, "Intermediates Path", 2048, false),
  201. "An optional path to a folder to use for the intermediate build files. Note that Visual Studio allows "
  202. "you to use macros in this path, e.g. \"$(TEMP)\\MyAppBuildFiles\\$(Configuration)\", which is a handy way to "
  203. "send them to the user's temp folder.");
  204. props.add (new ChoicePropertyComponent (warningLevelValue, "Warning Level",
  205. { "Low", "Medium", "High" },
  206. { 2, 3, 4 }),
  207. "The compilation warning level to use.");
  208. props.add (new ChoicePropertyComponent (warningsAreErrorsValue, "Treat Warnings as Errors"),
  209. "Enable this to treat compilation warnings as errors.");
  210. props.add (new ChoicePropertyComponent (useRuntimeLibDLLValue, "Runtime Library",
  211. { "Use static runtime", "Use DLL runtime" },
  212. { false, true }),
  213. "If the static runtime is selected then your app/plug-in will not be dependent upon users having Microsoft's redistributable "
  214. "C++ runtime installed. However, if you are linking libraries from different sources you must select the same type of runtime "
  215. "used by the libraries.");
  216. props.add (new ChoicePropertyComponent (multiProcessorCompilationValue, "Multi-Processor Compilation",
  217. { "Enabled", "Disabled" },
  218. { true, false }),
  219. "Allows the compiler to use of all the available processors, which can reduce compilation time. "
  220. "This is enabled by default and should only be disabled if you know what you are doing.");
  221. props.add (new ChoicePropertyComponent (enableIncrementalLinkingValue, "Incremental Linking"),
  222. "Enable to avoid linking from scratch for every new build. "
  223. "Disable to ensure that your final release build does not contain padding or thunks.");
  224. if (! isDebug())
  225. {
  226. props.add (new ChoicePropertyComponent (generateDebugSymbolsValue, "Force Generation of Debug Symbols"),
  227. "Enable this to force generation of debug symbols in a release configuration.");
  228. }
  229. props.add (new TextPropertyComponent (prebuildCommandValue, "Pre-build Command", 2048, true),
  230. "Some command that will be run before a build starts.");
  231. props.add (new TextPropertyComponent (postbuildCommandValue, "Post-build Command", 2048, true),
  232. "Some command that will be run after a build starts.");
  233. props.add (new ChoicePropertyComponent (generateManifestValue, "Generate Manifest"),
  234. "Enable this to generate a Manifest file.");
  235. props.add (new ChoicePropertyComponent (characterSetValue, "Character Set",
  236. { "MultiByte", "Unicode" },
  237. { "MultiByte", "Unicode" }),
  238. "Specifies the character set used when building.");
  239. }
  240. String getModuleLibraryArchName() const override
  241. {
  242. String result ("$(Platform)\\");
  243. result += isUsingRuntimeLibDLL() ? "MD" : "MT";
  244. if (isDebug())
  245. result += "d";
  246. return result;
  247. }
  248. void updateOldLTOSetting()
  249. {
  250. if (! isDebug() && config.getPropertyAsValue ("wholeProgramOptimisation", nullptr) != Value())
  251. linkTimeOptimisationValue = (static_cast<int> (config ["wholeProgramOptimisation"]) == 0);
  252. }
  253. private:
  254. ValueTreePropertyWithDefault warningLevelValue, warningsAreErrorsValue, prebuildCommandValue, postbuildCommandValue, generateDebugSymbolsValue,
  255. generateManifestValue, enableIncrementalLinkingValue, useRuntimeLibDLLValue, multiProcessorCompilationValue,
  256. intermediatesPathValue, characterSetValue, architectureTypeValue, fastMathValue, debugInformationFormatValue,
  257. pluginBinaryCopyStepValue;
  258. ValueTreePropertyWithDefault vstBinaryLocation, vst3BinaryLocation, rtasBinaryLocation, aaxBinaryLocation, unityPluginBinaryLocation;
  259. Value architectureValueToListenTo;
  260. //==============================================================================
  261. void addVisualStudioPluginInstallPathProperties (PropertyListBuilder& props)
  262. {
  263. auto isBuildingAnyPlugins = (project.shouldBuildVST() || project.shouldBuildVST3() || project.shouldBuildRTAS()
  264. || project.shouldBuildAAX() || project.shouldBuildUnityPlugin());
  265. if (isBuildingAnyPlugins)
  266. props.add (new ChoicePropertyComponent (pluginBinaryCopyStepValue, "Enable Plugin Copy Step"),
  267. "Enable this to copy plugin binaries to a specified folder after building.");
  268. if (project.shouldBuildVST3())
  269. props.add (new TextPropertyComponentWithEnablement (vst3BinaryLocation, pluginBinaryCopyStepValue, "VST3 Binary Location",
  270. 1024, false),
  271. "The folder in which the compiled VST3 binary should be placed.");
  272. if (project.shouldBuildRTAS())
  273. props.add (new TextPropertyComponentWithEnablement (rtasBinaryLocation, pluginBinaryCopyStepValue, "RTAS Binary Location",
  274. 1024, false),
  275. "The folder in which the compiled RTAS binary should be placed.");
  276. if (project.shouldBuildAAX())
  277. props.add (new TextPropertyComponentWithEnablement (aaxBinaryLocation, pluginBinaryCopyStepValue, "AAX Binary Location",
  278. 1024, false),
  279. "The folder in which the compiled AAX binary should be placed.");
  280. if (project.shouldBuildUnityPlugin())
  281. props.add (new TextPropertyComponentWithEnablement (unityPluginBinaryLocation, pluginBinaryCopyStepValue, "Unity Binary Location",
  282. 1024, false),
  283. "The folder in which the compiled Unity plugin binary and associated C# GUI script should be placed.");
  284. if (project.shouldBuildVST())
  285. props.add (new TextPropertyComponentWithEnablement (vstBinaryLocation, pluginBinaryCopyStepValue, "VST (Legacy) Binary Location",
  286. 1024, false),
  287. "The folder in which the compiled legacy VST binary should be placed.");
  288. }
  289. void setPluginBinaryCopyLocationDefaults()
  290. {
  291. vstBinaryLocation.setDefault ((is64Bit() ? "%ProgramW6432%" : "%programfiles(x86)%") + String ("\\Steinberg\\Vstplugins"));
  292. auto prefix = is64Bit() ? "%CommonProgramW6432%"
  293. : "%CommonProgramFiles(x86)%";
  294. vst3BinaryLocation.setDefault (prefix + String ("\\VST3"));
  295. rtasBinaryLocation.setDefault (prefix + String ("\\Digidesign\\DAE\\Plug-Ins"));
  296. aaxBinaryLocation.setDefault (prefix + String ("\\Avid\\Audio\\Plug-Ins"));
  297. }
  298. void valueChanged (Value&) override
  299. {
  300. setPluginBinaryCopyLocationDefaults();
  301. }
  302. };
  303. //==============================================================================
  304. class MSVCTargetBase : public build_tools::ProjectType::Target
  305. {
  306. public:
  307. MSVCTargetBase (build_tools::ProjectType::Target::Type targetType, const MSVCProjectExporterBase& exporter)
  308. : build_tools::ProjectType::Target (targetType), owner (exporter)
  309. {
  310. projectGuid = createGUID (owner.getProject().getProjectUIDString() + getName());
  311. }
  312. virtual ~MSVCTargetBase() {}
  313. String getProjectVersionString() const { return "10.00"; }
  314. String getProjectFileSuffix() const { return ".vcxproj"; }
  315. String getFiltersFileSuffix() const { return ".vcxproj.filters"; }
  316. String getTopLevelXmlEntity() const { return "Project"; }
  317. //==============================================================================
  318. void fillInProjectXml (XmlElement& projectXml) const
  319. {
  320. projectXml.setAttribute ("DefaultTargets", "Build");
  321. projectXml.setAttribute ("ToolsVersion", getOwner().getToolsVersion());
  322. projectXml.setAttribute ("xmlns", "http://schemas.microsoft.com/developer/msbuild/2003");
  323. {
  324. auto* configsGroup = projectXml.createNewChildElement ("ItemGroup");
  325. configsGroup->setAttribute ("Label", "ProjectConfigurations");
  326. for (ConstConfigIterator i (owner); i.next();)
  327. {
  328. auto& config = dynamic_cast<const MSVCBuildConfiguration&> (*i);
  329. auto* e = configsGroup->createNewChildElement ("ProjectConfiguration");
  330. e->setAttribute ("Include", config.createMSVCConfigName());
  331. e->createNewChildElement ("Configuration")->addTextElement (config.getName());
  332. e->createNewChildElement ("Platform")->addTextElement (config.is64Bit() ? config.get64BitArchName()
  333. : config.get32BitArchName());
  334. }
  335. }
  336. {
  337. auto* globals = projectXml.createNewChildElement ("PropertyGroup");
  338. globals->setAttribute ("Label", "Globals");
  339. globals->createNewChildElement ("ProjectGuid")->addTextElement (getProjectGuid());
  340. }
  341. {
  342. auto* imports = projectXml.createNewChildElement ("Import");
  343. imports->setAttribute ("Project", "$(VCTargetsPath)\\Microsoft.Cpp.Default.props");
  344. }
  345. for (ConstConfigIterator i (owner); i.next();)
  346. {
  347. auto& config = dynamic_cast<const MSVCBuildConfiguration&> (*i);
  348. auto* e = projectXml.createNewChildElement ("PropertyGroup");
  349. setConditionAttribute (*e, config);
  350. e->setAttribute ("Label", "Configuration");
  351. e->createNewChildElement ("ConfigurationType")->addTextElement (getProjectType());
  352. e->createNewChildElement ("UseOfMfc")->addTextElement ("false");
  353. e->createNewChildElement ("WholeProgramOptimization")->addTextElement (config.isLinkTimeOptimisationEnabled() ? "true"
  354. : "false");
  355. auto charSet = config.getCharacterSetString();
  356. if (charSet.isNotEmpty())
  357. e->createNewChildElement ("CharacterSet")->addTextElement (charSet);
  358. if (config.shouldLinkIncremental())
  359. e->createNewChildElement ("LinkIncremental")->addTextElement ("true");
  360. e->createNewChildElement ("PlatformToolset")->addTextElement (owner.getPlatformToolset());
  361. addWindowsTargetPlatformToConfig (*e);
  362. struct IntelLibraryInfo
  363. {
  364. String libraryKind;
  365. String configString;
  366. };
  367. for (const auto& info : { IntelLibraryInfo { owner.getIPPLibrary(), "UseIntelIPP" },
  368. IntelLibraryInfo { owner.getIPP1ALibrary(), "UseIntelIPP1A" },
  369. IntelLibraryInfo { owner.getMKL1ALibrary(), "UseInteloneMKL" } })
  370. {
  371. if (info.libraryKind.isNotEmpty())
  372. e->createNewChildElement (info.configString)->addTextElement (info.libraryKind);
  373. }
  374. }
  375. {
  376. auto* e = projectXml.createNewChildElement ("Import");
  377. e->setAttribute ("Project", "$(VCTargetsPath)\\Microsoft.Cpp.props");
  378. }
  379. {
  380. auto* e = projectXml.createNewChildElement ("ImportGroup");
  381. e->setAttribute ("Label", "ExtensionSettings");
  382. }
  383. {
  384. auto* e = projectXml.createNewChildElement ("ImportGroup");
  385. e->setAttribute ("Label", "PropertySheets");
  386. auto* p = e->createNewChildElement ("Import");
  387. p->setAttribute ("Project", "$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props");
  388. p->setAttribute ("Condition", "exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')");
  389. p->setAttribute ("Label", "LocalAppDataPlatform");
  390. }
  391. {
  392. auto* props = projectXml.createNewChildElement ("PropertyGroup");
  393. props->createNewChildElement ("_ProjectFileVersion")->addTextElement ("10.0.30319.1");
  394. props->createNewChildElement ("TargetExt")->addTextElement (getTargetSuffix());
  395. for (ConstConfigIterator i (owner); i.next();)
  396. {
  397. auto& config = dynamic_cast<const MSVCBuildConfiguration&> (*i);
  398. if (getConfigTargetPath (config).isNotEmpty())
  399. {
  400. auto* outdir = props->createNewChildElement ("OutDir");
  401. setConditionAttribute (*outdir, config);
  402. outdir->addTextElement (build_tools::windowsStylePath (getConfigTargetPath (config)) + "\\");
  403. }
  404. {
  405. auto* intdir = props->createNewChildElement ("IntDir");
  406. setConditionAttribute (*intdir, config);
  407. auto intermediatesPath = getIntermediatesPath (config);
  408. if (! intermediatesPath.endsWithChar (L'\\'))
  409. intermediatesPath += L'\\';
  410. intdir->addTextElement (build_tools::windowsStylePath (intermediatesPath));
  411. }
  412. {
  413. auto* targetName = props->createNewChildElement ("TargetName");
  414. setConditionAttribute (*targetName, config);
  415. targetName->addTextElement (msBuildEscape (config.getOutputFilename ("", false, type == UnityPlugIn)));
  416. }
  417. {
  418. auto* manifest = props->createNewChildElement ("GenerateManifest");
  419. setConditionAttribute (*manifest, config);
  420. manifest->addTextElement (config.shouldGenerateManifest() ? "true" : "false");
  421. }
  422. if (type != SharedCodeTarget)
  423. {
  424. auto librarySearchPaths = getLibrarySearchPaths (config);
  425. if (librarySearchPaths.size() > 0)
  426. {
  427. auto* libPath = props->createNewChildElement ("LibraryPath");
  428. setConditionAttribute (*libPath, config);
  429. libPath->addTextElement ("$(LibraryPath);" + librarySearchPaths.joinIntoString (";"));
  430. }
  431. }
  432. }
  433. }
  434. for (ConstConfigIterator i (owner); i.next();)
  435. {
  436. auto& config = dynamic_cast<const MSVCBuildConfiguration&> (*i);
  437. bool isDebug = config.isDebug();
  438. auto* group = projectXml.createNewChildElement ("ItemDefinitionGroup");
  439. setConditionAttribute (*group, config);
  440. {
  441. auto* midl = group->createNewChildElement ("Midl");
  442. midl->createNewChildElement ("PreprocessorDefinitions")->addTextElement (isDebug ? "_DEBUG;%(PreprocessorDefinitions)"
  443. : "NDEBUG;%(PreprocessorDefinitions)");
  444. midl->createNewChildElement ("MkTypLibCompatible")->addTextElement ("true");
  445. midl->createNewChildElement ("SuppressStartupBanner")->addTextElement ("true");
  446. midl->createNewChildElement ("TargetEnvironment")->addTextElement ("Win32");
  447. midl->createNewChildElement ("HeaderFileName");
  448. }
  449. bool isUsingEditAndContinue = false;
  450. const auto pdbFilename = getOwner().getIntDirFile (config, config.getOutputFilename (".pdb", true, type == UnityPlugIn));
  451. {
  452. auto* cl = group->createNewChildElement ("ClCompile");
  453. cl->createNewChildElement ("Optimization")->addTextElement (getOptimisationLevelString (config.getOptimisationLevelInt()));
  454. if (isDebug || config.shouldGenerateDebugSymbols())
  455. {
  456. cl->createNewChildElement ("DebugInformationFormat")
  457. ->addTextElement (config.getDebugInformationFormatString());
  458. }
  459. auto includePaths = getOwner().getHeaderSearchPaths (config);
  460. includePaths.addArray (getExtraSearchPaths());
  461. includePaths.add ("%(AdditionalIncludeDirectories)");
  462. cl->createNewChildElement ("AdditionalIncludeDirectories")->addTextElement (includePaths.joinIntoString (";"));
  463. cl->createNewChildElement ("PreprocessorDefinitions")->addTextElement (getPreprocessorDefs (config, ";") + ";%(PreprocessorDefinitions)");
  464. cl->createNewChildElement ("RuntimeLibrary")->addTextElement (config.isUsingRuntimeLibDLL() ? (isDebug ? "MultiThreadedDebugDLL" : "MultiThreadedDLL")
  465. : (isDebug ? "MultiThreadedDebug" : "MultiThreaded"));
  466. cl->createNewChildElement ("RuntimeTypeInfo")->addTextElement ("true");
  467. cl->createNewChildElement ("PrecompiledHeader")->addTextElement ("NotUsing");
  468. cl->createNewChildElement ("AssemblerListingLocation")->addTextElement ("$(IntDir)\\");
  469. cl->createNewChildElement ("ObjectFileName")->addTextElement ("$(IntDir)\\");
  470. cl->createNewChildElement ("ProgramDataBaseFileName")->addTextElement (pdbFilename);
  471. cl->createNewChildElement ("WarningLevel")->addTextElement ("Level" + String (config.getWarningLevel()));
  472. cl->createNewChildElement ("SuppressStartupBanner")->addTextElement ("true");
  473. cl->createNewChildElement ("MultiProcessorCompilation")->addTextElement (config.shouldUseMultiProcessorCompilation() ? "true" : "false");
  474. if (config.isFastMathEnabled())
  475. cl->createNewChildElement ("FloatingPointModel")->addTextElement ("Fast");
  476. auto extraFlags = getOwner().replacePreprocessorTokens (config, getOwner().getExtraCompilerFlagsString()).trim();
  477. if (extraFlags.isNotEmpty())
  478. cl->createNewChildElement ("AdditionalOptions")->addTextElement (extraFlags + " %(AdditionalOptions)");
  479. if (config.areWarningsTreatedAsErrors())
  480. cl->createNewChildElement ("TreatWarningAsError")->addTextElement ("true");
  481. auto cppStandard = owner.project.getCppStandardString();
  482. cl->createNewChildElement ("LanguageStandard")->addTextElement ("stdcpp" + cppStandard);
  483. }
  484. {
  485. auto* res = group->createNewChildElement ("ResourceCompile");
  486. res->createNewChildElement ("PreprocessorDefinitions")->addTextElement (isDebug ? "_DEBUG;%(PreprocessorDefinitions)"
  487. : "NDEBUG;%(PreprocessorDefinitions)");
  488. }
  489. auto externalLibraries = getExternalLibraries (config, getOwner().getExternalLibrariesStringArray());
  490. auto additionalDependencies = type != SharedCodeTarget && ! externalLibraries.isEmpty()
  491. ? externalLibraries.joinIntoString (";") + ";%(AdditionalDependencies)"
  492. : String();
  493. auto librarySearchPaths = config.getLibrarySearchPaths();
  494. auto additionalLibraryDirs = type != SharedCodeTarget && librarySearchPaths.size() > 0
  495. ? getOwner().replacePreprocessorTokens (config, librarySearchPaths.joinIntoString (";")) + ";%(AdditionalLibraryDirectories)"
  496. : String();
  497. {
  498. auto* link = group->createNewChildElement ("Link");
  499. link->createNewChildElement ("OutputFile")->addTextElement (getOutputFilePath (config, type == UnityPlugIn));
  500. link->createNewChildElement ("SuppressStartupBanner")->addTextElement ("true");
  501. link->createNewChildElement ("IgnoreSpecificDefaultLibraries")->addTextElement (isDebug ? "libcmt.lib; msvcrt.lib;;%(IgnoreSpecificDefaultLibraries)"
  502. : "%(IgnoreSpecificDefaultLibraries)");
  503. link->createNewChildElement ("GenerateDebugInformation")->addTextElement ((isDebug || config.shouldGenerateDebugSymbols()) ? "true" : "false");
  504. link->createNewChildElement ("ProgramDatabaseFile")->addTextElement (pdbFilename);
  505. link->createNewChildElement ("SubSystem")->addTextElement (type == ConsoleApp ? "Console" : "Windows");
  506. if (! config.is64Bit())
  507. link->createNewChildElement ("TargetMachine")->addTextElement ("MachineX86");
  508. if (isUsingEditAndContinue)
  509. link->createNewChildElement ("ImageHasSafeExceptionHandlers")->addTextElement ("false");
  510. if (! isDebug)
  511. {
  512. link->createNewChildElement ("OptimizeReferences")->addTextElement ("true");
  513. link->createNewChildElement ("EnableCOMDATFolding")->addTextElement ("true");
  514. }
  515. if (additionalLibraryDirs.isNotEmpty())
  516. link->createNewChildElement ("AdditionalLibraryDirectories")->addTextElement (additionalLibraryDirs);
  517. link->createNewChildElement ("LargeAddressAware")->addTextElement ("true");
  518. if (config.isLinkTimeOptimisationEnabled())
  519. link->createNewChildElement ("LinkTimeCodeGeneration")->addTextElement ("UseLinkTimeCodeGeneration");
  520. if (additionalDependencies.isNotEmpty())
  521. link->createNewChildElement ("AdditionalDependencies")->addTextElement (additionalDependencies);
  522. auto extraLinkerOptions = getOwner().getExtraLinkerFlagsString();
  523. if (extraLinkerOptions.isNotEmpty())
  524. link->createNewChildElement ("AdditionalOptions")->addTextElement (getOwner().replacePreprocessorTokens (config, extraLinkerOptions).trim()
  525. + " %(AdditionalOptions)");
  526. auto delayLoadedDLLs = getDelayLoadedDLLs();
  527. if (delayLoadedDLLs.isNotEmpty())
  528. link->createNewChildElement ("DelayLoadDLLs")->addTextElement (delayLoadedDLLs);
  529. auto moduleDefinitionsFile = getModuleDefinitions (config);
  530. if (moduleDefinitionsFile.isNotEmpty())
  531. link->createNewChildElement ("ModuleDefinitionFile")
  532. ->addTextElement (moduleDefinitionsFile);
  533. }
  534. {
  535. auto* bsc = group->createNewChildElement ("Bscmake");
  536. bsc->createNewChildElement ("SuppressStartupBanner")->addTextElement ("true");
  537. bsc->createNewChildElement ("OutputFile")->addTextElement (getOwner().getIntDirFile (config, config.getOutputFilename (".bsc", true, type == UnityPlugIn)));
  538. }
  539. if (type != SharedCodeTarget)
  540. {
  541. auto* lib = group->createNewChildElement ("Lib");
  542. if (additionalDependencies.isNotEmpty())
  543. lib->createNewChildElement ("AdditionalDependencies")->addTextElement (additionalDependencies);
  544. if (additionalLibraryDirs.isNotEmpty())
  545. lib->createNewChildElement ("AdditionalLibraryDirectories")->addTextElement (additionalLibraryDirs);
  546. }
  547. auto manifestFile = getOwner().getManifestPath();
  548. if (manifestFile.getRoot() != build_tools::RelativePath::unknown)
  549. {
  550. auto* bsc = group->createNewChildElement ("Manifest");
  551. bsc->createNewChildElement ("AdditionalManifestFiles")
  552. ->addTextElement (manifestFile.rebased (getOwner().getProject().getFile().getParentDirectory(),
  553. getOwner().getTargetFolder(),
  554. build_tools::RelativePath::buildTargetFolder).toWindowsStyle());
  555. }
  556. if (getTargetFileType() == staticLibrary && ! config.is64Bit())
  557. {
  558. auto* lib = group->createNewChildElement ("Lib");
  559. lib->createNewChildElement ("TargetMachine")->addTextElement ("MachineX86");
  560. }
  561. auto preBuild = getPreBuildSteps (config);
  562. if (preBuild.isNotEmpty())
  563. group->createNewChildElement ("PreBuildEvent")
  564. ->createNewChildElement ("Command")
  565. ->addTextElement (preBuild);
  566. auto postBuild = getPostBuildSteps (config);
  567. if (postBuild.isNotEmpty())
  568. group->createNewChildElement ("PostBuildEvent")
  569. ->createNewChildElement ("Command")
  570. ->addTextElement (postBuild);
  571. }
  572. std::unique_ptr<XmlElement> otherFilesGroup (new XmlElement ("ItemGroup"));
  573. {
  574. auto* cppFiles = projectXml.createNewChildElement ("ItemGroup");
  575. auto* headerFiles = projectXml.createNewChildElement ("ItemGroup");
  576. writePrecompiledHeaderFiles (*cppFiles);
  577. for (int i = 0; i < getOwner().getAllGroups().size(); ++i)
  578. {
  579. auto& group = getOwner().getAllGroups().getReference (i);
  580. if (group.getNumChildren() > 0)
  581. addFilesToCompile (group, *cppFiles, *headerFiles, *otherFilesGroup);
  582. }
  583. }
  584. if (getOwner().iconFile.existsAsFile())
  585. {
  586. auto* e = otherFilesGroup->createNewChildElement ("None");
  587. e->setAttribute ("Include", prependDot (getOwner().iconFile.getFileName()));
  588. }
  589. if (getOwner().packagesConfigFile.existsAsFile())
  590. {
  591. auto* e = otherFilesGroup->createNewChildElement ("None");
  592. e->setAttribute ("Include", getOwner().packagesConfigFile.getFileName());
  593. }
  594. if (otherFilesGroup->getFirstChildElement() != nullptr)
  595. projectXml.addChildElement (otherFilesGroup.release());
  596. if (type != SharedCodeTarget && getOwner().hasResourceFile())
  597. {
  598. auto* rcGroup = projectXml.createNewChildElement ("ItemGroup");
  599. auto* e = rcGroup->createNewChildElement ("ResourceCompile");
  600. e->setAttribute ("Include", prependDot (getOwner().rcFile.getFileName()));
  601. }
  602. {
  603. auto* e = projectXml.createNewChildElement ("Import");
  604. e->setAttribute ("Project", "$(VCTargetsPath)\\Microsoft.Cpp.targets");
  605. }
  606. {
  607. auto* importGroup = projectXml.createNewChildElement ("ImportGroup");
  608. importGroup->setAttribute ("Label", "ExtensionTargets");
  609. if (owner.shouldAddWebView2Package())
  610. {
  611. auto packageTargetsPath = "packages\\" + getWebView2PackageName() + "." + getWebView2PackageVersion()
  612. + "\\build\\native\\" + getWebView2PackageName() + ".targets";
  613. auto* e = importGroup->createNewChildElement ("Import");
  614. e->setAttribute ("Project", packageTargetsPath);
  615. e->setAttribute ("Condition", "Exists('" + packageTargetsPath + "')");
  616. }
  617. }
  618. }
  619. String getProjectType() const
  620. {
  621. auto targetFileType = getTargetFileType();
  622. if (targetFileType == executable) return "Application";
  623. if (targetFileType == staticLibrary) return "StaticLibrary";
  624. return "DynamicLibrary";
  625. }
  626. //==============================================================================
  627. static void setSourceFilePCHSettings (XmlElement& element, const File& pchFile, const String& option, const BuildConfiguration& config)
  628. {
  629. auto setConfigConditionAttribute = [&config] (XmlElement* elementToSet) -> XmlElement*
  630. {
  631. setConditionAttribute (*elementToSet, config);
  632. return elementToSet;
  633. };
  634. setConfigConditionAttribute (element.createNewChildElement ("PrecompiledHeader"))->addTextElement (option);
  635. setConfigConditionAttribute (element.createNewChildElement ("PrecompiledHeaderFile"))->addTextElement (pchFile.getFileName());
  636. setConfigConditionAttribute (element.createNewChildElement ("PrecompiledHeaderOutputFile"))->addTextElement ("$(Platform)\\$(Configuration)\\JucePrecompiledHeader.pch");
  637. setConfigConditionAttribute (element.createNewChildElement ("ForcedIncludeFiles"))->addTextElement (pchFile.getFileName());
  638. }
  639. void writePrecompiledHeaderFiles (XmlElement& cpps) const
  640. {
  641. for (ConstConfigIterator config (owner); config.next();)
  642. {
  643. if (config->shouldUsePrecompiledHeaderFile())
  644. {
  645. auto pchFileContent = config->getPrecompiledHeaderFileContent();
  646. if (pchFileContent.isNotEmpty())
  647. {
  648. auto pchFile = owner.getTargetFolder().getChildFile (config->getPrecompiledHeaderFilename()).withFileExtension (".h");
  649. build_tools::writeStreamToFile (pchFile, [&] (MemoryOutputStream& mo)
  650. {
  651. mo << pchFileContent;
  652. });
  653. auto pchSourceFile = pchFile.withFileExtension (".cpp");
  654. build_tools::writeStreamToFile (pchSourceFile, [this] (MemoryOutputStream& mo)
  655. {
  656. mo.setNewLineString (owner.getNewLineString());
  657. writeAutoGenWarningComment (mo);
  658. mo << " This is an empty source file generated by JUCE required for Visual Studio PCH." << newLine
  659. << newLine
  660. << "*/" << newLine
  661. << newLine;
  662. });
  663. auto* pchSourceElement = cpps.createNewChildElement ("ClCompile");
  664. pchSourceElement->setAttribute ("Include", prependDot (pchSourceFile.getFileName()));
  665. setSourceFilePCHSettings (*pchSourceElement, pchFile, "Create", *config);
  666. }
  667. }
  668. }
  669. }
  670. void addFilesToCompile (const Project::Item& projectItem, XmlElement& cpps, XmlElement& headers, XmlElement& otherFiles) const
  671. {
  672. auto targetType = (getOwner().getProject().isAudioPluginProject() ? type : SharedCodeTarget);
  673. if (projectItem.isGroup())
  674. {
  675. for (int i = 0; i < projectItem.getNumChildren(); ++i)
  676. addFilesToCompile (projectItem.getChild (i), cpps, headers, otherFiles);
  677. }
  678. else if (projectItem.shouldBeAddedToTargetProject() && projectItem.shouldBeAddedToTargetExporter (getOwner())
  679. && getOwner().getProject().getTargetTypeFromFilePath (projectItem.getFile(), true) == targetType)
  680. {
  681. build_tools::RelativePath path (projectItem.getFile(), getOwner().getTargetFolder(), build_tools::RelativePath::buildTargetFolder);
  682. jassert (path.getRoot() == build_tools::RelativePath::buildTargetFolder);
  683. if (path.hasFileExtension (cOrCppFileExtensions) || path.hasFileExtension (asmFileExtensions))
  684. {
  685. auto* e = cpps.createNewChildElement ("ClCompile");
  686. e->setAttribute ("Include", path.toWindowsStyle());
  687. if (shouldUseStdCall (path))
  688. e->createNewChildElement ("CallingConvention")->addTextElement ("StdCall");
  689. if (projectItem.shouldBeCompiled())
  690. {
  691. auto extraCompilerFlags = owner.compilerFlagSchemesMap[projectItem.getCompilerFlagSchemeString()].get().toString();
  692. if (shouldAddBigobjFlag (path))
  693. {
  694. const String bigobjFlag ("/bigobj");
  695. if (! extraCompilerFlags.contains (bigobjFlag))
  696. {
  697. extraCompilerFlags << " " << bigobjFlag;
  698. extraCompilerFlags.trim();
  699. }
  700. }
  701. if (extraCompilerFlags.isNotEmpty())
  702. e->createNewChildElement ("AdditionalOptions")->addTextElement (extraCompilerFlags + " %(AdditionalOptions)");
  703. if (! projectItem.shouldSkipPCH())
  704. {
  705. for (ConstConfigIterator i (owner); i.next();)
  706. {
  707. if (i->shouldUsePrecompiledHeaderFile())
  708. {
  709. auto pchFile = owner.getTargetFolder().getChildFile (i->getPrecompiledHeaderFilename()).withFileExtension (".h");
  710. if (pchFile.existsAsFile())
  711. setSourceFilePCHSettings (*e, pchFile, "Use", *i);
  712. }
  713. }
  714. }
  715. }
  716. else
  717. {
  718. e->createNewChildElement ("ExcludedFromBuild")->addTextElement ("true");
  719. }
  720. }
  721. else if (path.hasFileExtension (headerFileExtensions))
  722. {
  723. headers.createNewChildElement ("ClInclude")->setAttribute ("Include", path.toWindowsStyle());
  724. }
  725. else if (! path.hasFileExtension (objCFileExtensions))
  726. {
  727. otherFiles.createNewChildElement ("None")->setAttribute ("Include", path.toWindowsStyle());
  728. }
  729. }
  730. }
  731. static void setConditionAttribute (XmlElement& xml, const BuildConfiguration& config)
  732. {
  733. auto& msvcConfig = dynamic_cast<const MSVCBuildConfiguration&> (config);
  734. xml.setAttribute ("Condition", "'$(Configuration)|$(Platform)'=='" + msvcConfig.createMSVCConfigName() + "'");
  735. }
  736. //==============================================================================
  737. void addFilterGroup (XmlElement& groups, const String& path) const
  738. {
  739. auto* e = groups.createNewChildElement ("Filter");
  740. e->setAttribute ("Include", path);
  741. e->createNewChildElement ("UniqueIdentifier")->addTextElement (createGUID (path + "_guidpathsaltxhsdf"));
  742. }
  743. void addFileToFilter (const build_tools::RelativePath& file, const String& groupPath,
  744. XmlElement& cpps, XmlElement& headers, XmlElement& otherFiles) const
  745. {
  746. XmlElement* e = nullptr;
  747. if (file.hasFileExtension (headerFileExtensions))
  748. e = headers.createNewChildElement ("ClInclude");
  749. else if (file.hasFileExtension (sourceFileExtensions))
  750. e = cpps.createNewChildElement ("ClCompile");
  751. else
  752. e = otherFiles.createNewChildElement ("None");
  753. jassert (file.getRoot() == build_tools::RelativePath::buildTargetFolder);
  754. e->setAttribute ("Include", file.toWindowsStyle());
  755. e->createNewChildElement ("Filter")->addTextElement (groupPath);
  756. }
  757. bool addFilesToFilter (const Project::Item& projectItem, const String& path,
  758. XmlElement& cpps, XmlElement& headers, XmlElement& otherFiles, XmlElement& groups) const
  759. {
  760. auto targetType = (getOwner().getProject().isAudioPluginProject() ? type : SharedCodeTarget);
  761. if (projectItem.isGroup())
  762. {
  763. bool filesWereAdded = false;
  764. for (int i = 0; i < projectItem.getNumChildren(); ++i)
  765. if (addFilesToFilter (projectItem.getChild(i),
  766. (path.isEmpty() ? String() : (path + "\\")) + projectItem.getChild(i).getName(),
  767. cpps, headers, otherFiles, groups))
  768. filesWereAdded = true;
  769. if (filesWereAdded)
  770. addFilterGroup (groups, path);
  771. return filesWereAdded;
  772. }
  773. else if (projectItem.shouldBeAddedToTargetProject()
  774. && projectItem.shouldBeAddedToTargetExporter (getOwner())
  775. && getOwner().getProject().getTargetTypeFromFilePath (projectItem.getFile(), true) == targetType)
  776. {
  777. build_tools::RelativePath relativePath (projectItem.getFile(),
  778. getOwner().getTargetFolder(),
  779. build_tools::RelativePath::buildTargetFolder);
  780. jassert (relativePath.getRoot() == build_tools::RelativePath::buildTargetFolder);
  781. addFileToFilter (relativePath, path.upToLastOccurrenceOf ("\\", false, false), cpps, headers, otherFiles);
  782. return true;
  783. }
  784. return false;
  785. }
  786. void fillInFiltersXml (XmlElement& filterXml) const
  787. {
  788. filterXml.setAttribute ("ToolsVersion", getOwner().getToolsVersion());
  789. filterXml.setAttribute ("xmlns", "http://schemas.microsoft.com/developer/msbuild/2003");
  790. auto* groupsXml = filterXml.createNewChildElement ("ItemGroup");
  791. auto* cpps = filterXml.createNewChildElement ("ItemGroup");
  792. auto* headers = filterXml.createNewChildElement ("ItemGroup");
  793. std::unique_ptr<XmlElement> otherFilesGroup (new XmlElement ("ItemGroup"));
  794. for (int i = 0; i < getOwner().getAllGroups().size(); ++i)
  795. {
  796. auto& group = getOwner().getAllGroups().getReference(i);
  797. if (group.getNumChildren() > 0)
  798. addFilesToFilter (group, group.getName(), *cpps, *headers, *otherFilesGroup, *groupsXml);
  799. }
  800. if (getOwner().iconFile.existsAsFile())
  801. {
  802. auto* e = otherFilesGroup->createNewChildElement ("None");
  803. e->setAttribute ("Include", prependDot (getOwner().iconFile.getFileName()));
  804. e->createNewChildElement ("Filter")->addTextElement (ProjectSaver::getJuceCodeGroupName());
  805. }
  806. if (getOwner().packagesConfigFile.existsAsFile())
  807. {
  808. auto* e = otherFilesGroup->createNewChildElement ("None");
  809. e->setAttribute ("Include", getOwner().packagesConfigFile.getFileName());
  810. }
  811. if (otherFilesGroup->getFirstChildElement() != nullptr)
  812. filterXml.addChildElement (otherFilesGroup.release());
  813. if (type != SharedCodeTarget && getOwner().hasResourceFile())
  814. {
  815. auto* rcGroup = filterXml.createNewChildElement ("ItemGroup");
  816. auto* e = rcGroup->createNewChildElement ("ResourceCompile");
  817. e->setAttribute ("Include", prependDot (getOwner().rcFile.getFileName()));
  818. e->createNewChildElement ("Filter")->addTextElement (ProjectSaver::getJuceCodeGroupName());
  819. }
  820. }
  821. const MSVCProjectExporterBase& getOwner() const { return owner; }
  822. const String& getProjectGuid() const { return projectGuid; }
  823. //==============================================================================
  824. void writeProjectFile()
  825. {
  826. {
  827. XmlElement projectXml (getTopLevelXmlEntity());
  828. fillInProjectXml (projectXml);
  829. writeXmlOrThrow (projectXml, getVCProjFile(), "UTF-8", 10);
  830. }
  831. {
  832. XmlElement filtersXml (getTopLevelXmlEntity());
  833. fillInFiltersXml (filtersXml);
  834. writeXmlOrThrow (filtersXml, getVCProjFiltersFile(), "UTF-8", 100);
  835. }
  836. }
  837. String getSolutionTargetPath (const BuildConfiguration& config) const
  838. {
  839. auto binaryPath = config.getTargetBinaryRelativePathString().trim();
  840. if (binaryPath.isEmpty())
  841. return "$(SolutionDir)$(Platform)\\$(Configuration)";
  842. build_tools::RelativePath binaryRelPath (binaryPath, build_tools::RelativePath::projectFolder);
  843. if (binaryRelPath.isAbsolute())
  844. return binaryRelPath.toWindowsStyle();
  845. return prependDot (binaryRelPath.rebased (getOwner().projectFolder,
  846. getOwner().getTargetFolder(),
  847. build_tools::RelativePath::buildTargetFolder)
  848. .toWindowsStyle());
  849. }
  850. String getConfigTargetPath (const BuildConfiguration& config) const
  851. {
  852. auto solutionTargetFolder = getSolutionTargetPath (config);
  853. return solutionTargetFolder + "\\" + getName();
  854. }
  855. String getIntermediatesPath (const MSVCBuildConfiguration& config) const
  856. {
  857. auto intDir = (config.getIntermediatesPathString().isNotEmpty() ? config.getIntermediatesPathString()
  858. : "$(Platform)\\$(Configuration)");
  859. if (! intDir.endsWithChar (L'\\'))
  860. intDir += L'\\';
  861. return intDir + getName();
  862. }
  863. static const char* getOptimisationLevelString (int level)
  864. {
  865. switch (level)
  866. {
  867. case optimiseMinSize: return "MinSpace";
  868. case optimiseMaxSpeed: return "MaxSpeed";
  869. case optimiseFull: return "Full";
  870. default: return "Disabled";
  871. }
  872. }
  873. String getTargetSuffix() const
  874. {
  875. auto fileType = getTargetFileType();
  876. if (fileType == executable) return ".exe";
  877. if (fileType == staticLibrary) return ".lib";
  878. if (fileType == sharedLibraryOrDLL) return ".dll";
  879. if (fileType == pluginBundle)
  880. {
  881. if (type == VST3PlugIn) return ".vst3";
  882. if (type == AAXPlugIn) return ".aaxdll";
  883. if (type == RTASPlugIn) return ".dpm";
  884. return ".dll";
  885. }
  886. return {};
  887. }
  888. XmlElement* createToolElement (XmlElement& parent, const String& toolName) const
  889. {
  890. auto* e = parent.createNewChildElement ("Tool");
  891. e->setAttribute ("Name", toolName);
  892. return e;
  893. }
  894. String getPreprocessorDefs (const BuildConfiguration& config, const String& joinString) const
  895. {
  896. auto defines = getOwner().msvcExtraPreprocessorDefs;
  897. defines.set ("WIN32", "");
  898. defines.set ("_WINDOWS", "");
  899. if (config.isDebug())
  900. {
  901. defines.set ("DEBUG", "");
  902. defines.set ("_DEBUG", "");
  903. }
  904. else
  905. {
  906. defines.set ("NDEBUG", "");
  907. }
  908. defines = mergePreprocessorDefs (defines, getOwner().getAllPreprocessorDefs (config, type));
  909. addExtraPreprocessorDefines (defines);
  910. if (getTargetFileType() == staticLibrary || getTargetFileType() == sharedLibraryOrDLL)
  911. defines.set("_LIB", "");
  912. StringArray result;
  913. for (int i = 0; i < defines.size(); ++i)
  914. {
  915. auto def = defines.getAllKeys()[i];
  916. auto value = defines.getAllValues()[i];
  917. if (value.isNotEmpty())
  918. def << "=" << value;
  919. result.add (def);
  920. }
  921. return result.joinIntoString (joinString);
  922. }
  923. //==============================================================================
  924. build_tools::RelativePath getAAXIconFile() const
  925. {
  926. build_tools::RelativePath aaxSDK (owner.getAAXPathString(), build_tools::RelativePath::projectFolder);
  927. build_tools::RelativePath projectIcon ("icon.ico", build_tools::RelativePath::buildTargetFolder);
  928. if (getOwner().getTargetFolder().getChildFile ("icon.ico").existsAsFile())
  929. return projectIcon.rebased (getOwner().getTargetFolder(),
  930. getOwner().getProject().getProjectFolder(),
  931. build_tools::RelativePath::projectFolder);
  932. return aaxSDK.getChildFile ("Utilities").getChildFile ("PlugIn.ico");
  933. }
  934. String getExtraPostBuildSteps (const MSVCBuildConfiguration& config) const
  935. {
  936. if (type == AAXPlugIn)
  937. {
  938. build_tools::RelativePath aaxSDK (owner.getAAXPathString(), build_tools::RelativePath::projectFolder);
  939. build_tools::RelativePath aaxLibsFolder = aaxSDK.getChildFile ("Libs");
  940. build_tools::RelativePath bundleScript = aaxSDK.getChildFile ("Utilities").getChildFile ("CreatePackage.bat");
  941. build_tools::RelativePath iconFilePath = getAAXIconFile();
  942. auto outputFilename = config.getOutputFilename (".aaxplugin", true, false);
  943. auto bundleDir = getOwner().getOutDirFile (config, outputFilename);
  944. auto bundleContents = bundleDir + "\\Contents";
  945. auto archDir = bundleContents + String ("\\") + (config.is64Bit() ? "x64" : "Win32");
  946. auto executablePath = archDir + String ("\\") + outputFilename;
  947. auto pkgScript = String ("copy /Y ") + getOutputFilePath (config, false).quoted() + String (" ") + executablePath.quoted() + String ("\r\ncall ")
  948. + createRebasedPath (bundleScript) + String (" ") + archDir.quoted() + String (" ") + createRebasedPath (iconFilePath);
  949. if (config.isPluginBinaryCopyStepEnabled())
  950. return pkgScript + "\r\n" + "xcopy " + bundleDir.quoted() + " "
  951. + String (config.getAAXBinaryLocationString() + "\\" + outputFilename + "\\").quoted() + " /E /H /K /R /Y";
  952. return pkgScript;
  953. }
  954. else if (type == UnityPlugIn)
  955. {
  956. build_tools::RelativePath scriptPath (config.project.getGeneratedCodeFolder().getChildFile (config.project.getUnityScriptName()),
  957. getOwner().getTargetFolder(),
  958. build_tools::RelativePath::projectFolder);
  959. auto pkgScript = String ("copy /Y ") + scriptPath.toWindowsStyle().quoted() + " \"$(OutDir)\"";
  960. if (config.isPluginBinaryCopyStepEnabled())
  961. {
  962. auto copyLocation = config.getUnityPluginBinaryLocationString();
  963. pkgScript += "\r\ncopy /Y \"$(OutDir)$(TargetFileName)\" " + String (copyLocation + "\\$(TargetFileName)").quoted();
  964. pkgScript += "\r\ncopy /Y " + String ("$(OutDir)" + config.project.getUnityScriptName()).quoted() + " " + String (copyLocation + "\\" + config.project.getUnityScriptName()).quoted();
  965. }
  966. return pkgScript;
  967. }
  968. else if (config.isPluginBinaryCopyStepEnabled())
  969. {
  970. auto copyScript = String ("copy /Y \"$(OutDir)$(TargetFileName)\"") + String (" \"$COPYDIR$\\$(TargetFileName)\"");
  971. if (type == VSTPlugIn) return copyScript.replace ("$COPYDIR$", config.getVSTBinaryLocationString());
  972. if (type == VST3PlugIn) return copyScript.replace ("$COPYDIR$", config.getVST3BinaryLocationString());
  973. if (type == RTASPlugIn) return copyScript.replace ("$COPYDIR$", config.getRTASBinaryLocationString());
  974. }
  975. return {};
  976. }
  977. String getExtraPreBuildSteps (const MSVCBuildConfiguration& config) const
  978. {
  979. if (type == AAXPlugIn)
  980. {
  981. String script;
  982. auto bundleDir = getOwner().getOutDirFile (config, config.getOutputFilename (".aaxplugin", false, false));
  983. auto bundleContents = bundleDir + "\\Contents";
  984. auto archDir = bundleContents + String ("\\") + (config.is64Bit() ? "x64" : "Win32");
  985. for (auto& folder : StringArray { bundleDir, bundleContents, archDir })
  986. script += String ("if not exist \"") + folder + String ("\" mkdir \"") + folder + String ("\"\r\n");
  987. return script;
  988. }
  989. return {};
  990. }
  991. String getPostBuildSteps (const MSVCBuildConfiguration& config) const
  992. {
  993. auto postBuild = config.getPostbuildCommandString().replace ("\n", "\r\n");;
  994. auto extraPostBuild = getExtraPostBuildSteps (config);
  995. return postBuild + String (postBuild.isNotEmpty() && extraPostBuild.isNotEmpty() ? "\r\n" : "") + extraPostBuild;
  996. }
  997. String getPreBuildSteps (const MSVCBuildConfiguration& config) const
  998. {
  999. auto preBuild = config.getPrebuildCommandString().replace ("\n", "\r\n");;
  1000. auto extraPreBuild = getExtraPreBuildSteps (config);
  1001. return preBuild + String (preBuild.isNotEmpty() && extraPreBuild.isNotEmpty() ? "\r\n" : "") + extraPreBuild;
  1002. }
  1003. void addExtraPreprocessorDefines (StringPairArray& defines) const
  1004. {
  1005. if (type == AAXPlugIn)
  1006. {
  1007. auto aaxLibsFolder = build_tools::RelativePath (owner.getAAXPathString(), build_tools::RelativePath::projectFolder).getChildFile ("Libs");
  1008. defines.set ("JucePlugin_AAXLibs_path", createRebasedPath (aaxLibsFolder));
  1009. }
  1010. else if (type == RTASPlugIn)
  1011. {
  1012. build_tools::RelativePath rtasFolder (owner.getRTASPathString(), build_tools::RelativePath::projectFolder);
  1013. defines.set ("JucePlugin_WinBag_path", createRebasedPath (rtasFolder.getChildFile ("WinBag")));
  1014. }
  1015. }
  1016. String getExtraLinkerFlags() const
  1017. {
  1018. if (type == RTASPlugIn)
  1019. return "/FORCE:multiple";
  1020. return {};
  1021. }
  1022. StringArray getExtraSearchPaths() const
  1023. {
  1024. StringArray searchPaths;
  1025. if (type == RTASPlugIn)
  1026. {
  1027. build_tools::RelativePath rtasFolder (owner.getRTASPathString(), build_tools::RelativePath::projectFolder);
  1028. static const char* p[] = { "AlturaPorts/TDMPlugins/PluginLibrary/EffectClasses",
  1029. "AlturaPorts/TDMPlugins/PluginLibrary/ProcessClasses",
  1030. "AlturaPorts/TDMPlugins/PluginLibrary/ProcessClasses/Interfaces",
  1031. "AlturaPorts/TDMPlugins/PluginLibrary/Utilities",
  1032. "AlturaPorts/TDMPlugins/PluginLibrary/RTASP_Adapt",
  1033. "AlturaPorts/TDMPlugins/PluginLibrary/CoreClasses",
  1034. "AlturaPorts/TDMPlugins/PluginLibrary/Controls",
  1035. "AlturaPorts/TDMPlugins/PluginLibrary/Meters",
  1036. "AlturaPorts/TDMPlugins/PluginLibrary/ViewClasses",
  1037. "AlturaPorts/TDMPlugins/PluginLibrary/DSPClasses",
  1038. "AlturaPorts/TDMPlugins/PluginLibrary/Interfaces",
  1039. "AlturaPorts/TDMPlugins/common",
  1040. "AlturaPorts/TDMPlugins/common/Platform",
  1041. "AlturaPorts/TDMPlugins/common/Macros",
  1042. "AlturaPorts/TDMPlugins/SignalProcessing/Public",
  1043. "AlturaPorts/TDMPlugIns/DSPManager/Interfaces",
  1044. "AlturaPorts/SADriver/Interfaces",
  1045. "AlturaPorts/DigiPublic/Interfaces",
  1046. "AlturaPorts/DigiPublic",
  1047. "AlturaPorts/Fic/Interfaces/DAEClient",
  1048. "AlturaPorts/NewFileLibs/Cmn",
  1049. "AlturaPorts/NewFileLibs/DOA",
  1050. "AlturaPorts/AlturaSource/PPC_H",
  1051. "AlturaPorts/AlturaSource/AppSupport",
  1052. "AvidCode/AVX2sdk/AVX/avx2/avx2sdk/inc",
  1053. "xplat/AVX/avx2/avx2sdk/inc" };
  1054. for (auto* path : p)
  1055. searchPaths.add (createRebasedPath (rtasFolder.getChildFile (path)));
  1056. }
  1057. return searchPaths;
  1058. }
  1059. String getBinaryNameWithSuffix (const MSVCBuildConfiguration& config, bool forceUnityPrefix) const
  1060. {
  1061. return config.getOutputFilename (getTargetSuffix(), true, forceUnityPrefix);
  1062. }
  1063. String getOutputFilePath (const MSVCBuildConfiguration& config, bool forceUnityPrefix) const
  1064. {
  1065. return getOwner().getOutDirFile (config, getBinaryNameWithSuffix (config, forceUnityPrefix));
  1066. }
  1067. StringArray getLibrarySearchPaths (const BuildConfiguration& config) const
  1068. {
  1069. auto librarySearchPaths = config.getLibrarySearchPaths();
  1070. if (type != SharedCodeTarget)
  1071. if (auto* shared = getOwner().getSharedCodeTarget())
  1072. librarySearchPaths.add (shared->getConfigTargetPath (config));
  1073. return librarySearchPaths;
  1074. }
  1075. StringArray getExternalLibraries (const MSVCBuildConfiguration& config, const StringArray& otherLibs) const
  1076. {
  1077. const auto sharedCodeLib = [&]() -> StringArray
  1078. {
  1079. if (type != SharedCodeTarget)
  1080. if (auto* shared = getOwner().getSharedCodeTarget())
  1081. return { shared->getBinaryNameWithSuffix (config, false) };
  1082. return {};
  1083. }();
  1084. auto result = otherLibs;
  1085. result.addArray (getOwner().getModuleLibs());
  1086. result.addArray (sharedCodeLib);
  1087. for (auto& i : result)
  1088. i = msBuildEscape (getOwner().replacePreprocessorTokens (config, i).trim());
  1089. return result;
  1090. }
  1091. String getDelayLoadedDLLs() const
  1092. {
  1093. auto delayLoadedDLLs = getOwner().msvcDelayLoadedDLLs;
  1094. if (type == RTASPlugIn)
  1095. delayLoadedDLLs += "DAE.dll; DigiExt.dll; DSI.dll; PluginLib.dll; "
  1096. "DSPManager.dll; DSPManager.dll; DSPManagerClientLib.dll; RTASClientLib.dll";
  1097. return delayLoadedDLLs;
  1098. }
  1099. String getModuleDefinitions (const MSVCBuildConfiguration& config) const
  1100. {
  1101. auto moduleDefinitions = config.config [Ids::msvcModuleDefinitionFile].toString();
  1102. if (moduleDefinitions.isNotEmpty())
  1103. return moduleDefinitions;
  1104. if (type == RTASPlugIn)
  1105. {
  1106. auto& exp = getOwner();
  1107. auto moduleDefPath
  1108. = build_tools::RelativePath (exp.getPathForModuleString ("juce_audio_plugin_client"), build_tools::RelativePath::projectFolder)
  1109. .getChildFile ("juce_audio_plugin_client").getChildFile ("RTAS").getChildFile ("juce_RTAS_WinExports.def");
  1110. return prependDot (moduleDefPath.rebased (exp.getProject().getProjectFolder(),
  1111. exp.getTargetFolder(),
  1112. build_tools::RelativePath::buildTargetFolder).toWindowsStyle());
  1113. }
  1114. return {};
  1115. }
  1116. File getVCProjFile() const { return getOwner().getProjectFile (getProjectFileSuffix(), getName()); }
  1117. File getVCProjFiltersFile() const { return getOwner().getProjectFile (getFiltersFileSuffix(), getName()); }
  1118. String createRebasedPath (const build_tools::RelativePath& path) const { return getOwner().createRebasedPath (path); }
  1119. void addWindowsTargetPlatformToConfig (XmlElement& e) const
  1120. {
  1121. auto target = owner.getWindowsTargetPlatformVersion();
  1122. if (target == "Latest")
  1123. {
  1124. auto* child = e.createNewChildElement ("WindowsTargetPlatformVersion");
  1125. child->setAttribute ("Condition", "'$(WindowsTargetPlatformVersion)' == ''");
  1126. child->addTextElement ("$([Microsoft.Build.Utilities.ToolLocationHelper]::GetLatestSDKTargetPlatformVersion('Windows', '10.0'))");
  1127. }
  1128. else
  1129. {
  1130. e.createNewChildElement ("WindowsTargetPlatformVersion")->addTextElement (target);
  1131. }
  1132. }
  1133. protected:
  1134. const MSVCProjectExporterBase& owner;
  1135. String projectGuid;
  1136. };
  1137. //==============================================================================
  1138. bool usesMMFiles() const override { return false; }
  1139. bool canCopeWithDuplicateFiles() override { return false; }
  1140. bool supportsUserDefinedConfigurations() const override { return true; }
  1141. bool isXcode() const override { return false; }
  1142. bool isVisualStudio() const override { return true; }
  1143. bool isCodeBlocks() const override { return false; }
  1144. bool isMakefile() const override { return false; }
  1145. bool isAndroidStudio() const override { return false; }
  1146. bool isCLion() const override { return false; }
  1147. bool isAndroid() const override { return false; }
  1148. bool isWindows() const override { return true; }
  1149. bool isLinux() const override { return false; }
  1150. bool isOSX() const override { return false; }
  1151. bool isiOS() const override { return false; }
  1152. bool supportsPrecompiledHeaders() const override { return true; }
  1153. String getNewLineString() const override { return "\r\n"; }
  1154. bool supportsTargetType (build_tools::ProjectType::Target::Type type) const override
  1155. {
  1156. switch (type)
  1157. {
  1158. case build_tools::ProjectType::Target::StandalonePlugIn:
  1159. case build_tools::ProjectType::Target::GUIApp:
  1160. case build_tools::ProjectType::Target::ConsoleApp:
  1161. case build_tools::ProjectType::Target::StaticLibrary:
  1162. case build_tools::ProjectType::Target::SharedCodeTarget:
  1163. case build_tools::ProjectType::Target::AggregateTarget:
  1164. case build_tools::ProjectType::Target::VSTPlugIn:
  1165. case build_tools::ProjectType::Target::VST3PlugIn:
  1166. case build_tools::ProjectType::Target::AAXPlugIn:
  1167. case build_tools::ProjectType::Target::RTASPlugIn:
  1168. case build_tools::ProjectType::Target::UnityPlugIn:
  1169. case build_tools::ProjectType::Target::DynamicLibrary:
  1170. return true;
  1171. case build_tools::ProjectType::Target::AudioUnitPlugIn:
  1172. case build_tools::ProjectType::Target::AudioUnitv3PlugIn:
  1173. case build_tools::ProjectType::Target::unspecified:
  1174. default:
  1175. break;
  1176. }
  1177. return false;
  1178. }
  1179. //==============================================================================
  1180. build_tools::RelativePath getManifestPath() const
  1181. {
  1182. auto path = manifestFileValue.get().toString();
  1183. return path.isEmpty() ? build_tools::RelativePath()
  1184. : build_tools::RelativePath (path, build_tools::RelativePath::projectFolder);
  1185. }
  1186. //==============================================================================
  1187. bool launchProject() override
  1188. {
  1189. #if JUCE_WINDOWS
  1190. return getSLNFile().startAsProcess();
  1191. #else
  1192. return false;
  1193. #endif
  1194. }
  1195. bool canLaunchProject() override
  1196. {
  1197. #if JUCE_WINDOWS
  1198. return true;
  1199. #else
  1200. return false;
  1201. #endif
  1202. }
  1203. void createExporterProperties (PropertyListBuilder& props) override
  1204. {
  1205. props.add (new TextPropertyComponent (manifestFileValue, "Manifest file", 8192, false),
  1206. "Path to a manifest input file which should be linked into your binary (path is relative to jucer file).");
  1207. props.add (new ChoicePropertyComponent (IPPLibraryValue, "(deprecated) Use IPP Library",
  1208. { "No", "Yes (Default Linking)", "Multi-Threaded Static Library", "Single-Threaded Static Library", "Multi-Threaded DLL", "Single-Threaded DLL" },
  1209. { var(), "true", "Parallel_Static", "Sequential", "Parallel_Dynamic", "Sequential_Dynamic" }),
  1210. "This option is deprecated, use the \"Use IPP Library (oneAPI)\" option instead. "
  1211. "Enable this to use Intel's Integrated Performance Primitives library, if you have an older version that was not supplied in the oneAPI toolkit.");
  1212. props.add (new ChoicePropertyComponent (IPP1ALibraryValue, "Use IPP Library (oneAPI)",
  1213. { "No", "Yes (Default Linking)", "Static Library", "Dynamic Library" },
  1214. { var(), "true", "Static_Library", "Dynamic_Library" }),
  1215. "Enable this to use Intel's Integrated Performance Primitives library, supplied as part of the oneAPI toolkit.");
  1216. props.add (new ChoicePropertyComponent (MKL1ALibraryValue, "Use MKL Library (oneAPI)",
  1217. { "No", "Parallel", "Sequential", "Cluster" },
  1218. { var(), "Parallel", "Sequential", "Cluster" }),
  1219. "Enable this to use Intel's MKL library, supplied as part of the oneAPI toolkit.");
  1220. {
  1221. auto isWindows10SDK = getVisualStudioVersion() > 14;
  1222. props.add (new TextPropertyComponent (targetPlatformVersion, "Windows Target Platform", 20, false),
  1223. String ("Specifies the version of the Windows SDK that will be used when building this project. ")
  1224. + (isWindows10SDK ? "Leave this field empty to use the latest Windows 10 SDK installed on the build machine."
  1225. : "The default value for this exporter is " + getDefaultWindowsTargetPlatformVersion()));
  1226. }
  1227. }
  1228. enum OptimisationLevel
  1229. {
  1230. optimisationOff = 1,
  1231. optimiseMinSize = 2,
  1232. optimiseFull = 3,
  1233. optimiseMaxSpeed = 4
  1234. };
  1235. //==============================================================================
  1236. void addPlatformSpecificSettingsForProjectType (const build_tools::ProjectType& type) override
  1237. {
  1238. msvcExtraPreprocessorDefs.set ("_CRT_SECURE_NO_WARNINGS", "");
  1239. if (type.isCommandLineApp())
  1240. msvcExtraPreprocessorDefs.set("_CONSOLE", "");
  1241. callForAllSupportedTargets ([this] (build_tools::ProjectType::Target::Type targetType)
  1242. {
  1243. if (targetType != build_tools::ProjectType::Target::AggregateTarget)
  1244. targets.add (new MSVCTargetBase (targetType, *this));
  1245. });
  1246. // If you hit this assert, you tried to generate a project for an exporter
  1247. // that does not support any of your targets!
  1248. jassert (targets.size() > 0);
  1249. }
  1250. const MSVCTargetBase* getSharedCodeTarget() const
  1251. {
  1252. for (auto target : targets)
  1253. if (target->type == build_tools::ProjectType::Target::SharedCodeTarget)
  1254. return target;
  1255. return nullptr;
  1256. }
  1257. bool hasTarget (build_tools::ProjectType::Target::Type type) const
  1258. {
  1259. for (auto target : targets)
  1260. if (target->type == type)
  1261. return true;
  1262. return false;
  1263. }
  1264. static void createRCFile (const Project& p, const File& iconFile, const File& rcFile)
  1265. {
  1266. build_tools::ResourceRcOptions resourceRc;
  1267. resourceRc.version = p.getVersionString();
  1268. resourceRc.companyName = p.getCompanyNameString();
  1269. resourceRc.companyCopyright = p.getCompanyCopyrightString();
  1270. resourceRc.projectName = p.getProjectNameString();
  1271. resourceRc.icon = iconFile;
  1272. resourceRc.write (rcFile);
  1273. }
  1274. private:
  1275. //==============================================================================
  1276. String createRebasedPath (const build_tools::RelativePath& path) const
  1277. {
  1278. auto rebasedPath = rebaseFromProjectFolderToBuildTarget (path).toWindowsStyle();
  1279. return getVisualStudioVersion() < 10 // (VS10 automatically adds escape characters to the quotes for this definition)
  1280. ? CppTokeniserFunctions::addEscapeChars (rebasedPath.quoted())
  1281. : CppTokeniserFunctions::addEscapeChars (rebasedPath).quoted();
  1282. }
  1283. protected:
  1284. //==============================================================================
  1285. mutable File rcFile, iconFile, packagesConfigFile;
  1286. OwnedArray<MSVCTargetBase> targets;
  1287. ValueTreePropertyWithDefault IPPLibraryValue,
  1288. IPP1ALibraryValue,
  1289. MKL1ALibraryValue,
  1290. platformToolsetValue,
  1291. targetPlatformVersion,
  1292. manifestFileValue;
  1293. File getProjectFile (const String& extension, const String& target) const
  1294. {
  1295. auto filename = project.getProjectFilenameRootString();
  1296. if (target.isNotEmpty())
  1297. filename += String ("_") + target.removeCharacters (" ");
  1298. return getTargetFolder().getChildFile (filename).withFileExtension (extension);
  1299. }
  1300. File getSLNFile() const { return getProjectFile (".sln", String()); }
  1301. static String prependIfNotAbsolute (const String& file, const char* prefix)
  1302. {
  1303. if (File::isAbsolutePath (file) || file.startsWithChar ('$'))
  1304. prefix = "";
  1305. return prefix + build_tools::windowsStylePath (file);
  1306. }
  1307. String getIntDirFile (const BuildConfiguration& config, const String& file) const { return prependIfNotAbsolute (replacePreprocessorTokens (config, file), "$(IntDir)\\"); }
  1308. String getOutDirFile (const BuildConfiguration& config, const String& file) const { return prependIfNotAbsolute (replacePreprocessorTokens (config, file), "$(OutDir)\\"); }
  1309. BuildConfiguration::Ptr createBuildConfig (const ValueTree& v) const override
  1310. {
  1311. return *new MSVCBuildConfiguration (project, v, *this);
  1312. }
  1313. StringArray getHeaderSearchPaths (const BuildConfiguration& config) const
  1314. {
  1315. auto searchPaths = extraSearchPaths;
  1316. searchPaths.addArray (config.getHeaderSearchPaths());
  1317. return getCleanedStringArray (searchPaths);
  1318. }
  1319. String getSharedCodeGuid() const
  1320. {
  1321. String sharedCodeGuid;
  1322. for (int i = 0; i < targets.size(); ++i)
  1323. if (auto* target = targets[i])
  1324. if (target->type == build_tools::ProjectType::Target::SharedCodeTarget)
  1325. return target->getProjectGuid();
  1326. return {};
  1327. }
  1328. //==============================================================================
  1329. void writeProjectDependencies (OutputStream& out) const
  1330. {
  1331. auto sharedCodeGuid = getSharedCodeGuid();
  1332. for (int addingOtherTargets = 0; addingOtherTargets < (sharedCodeGuid.isNotEmpty() ? 2 : 1); ++addingOtherTargets)
  1333. {
  1334. for (int i = 0; i < targets.size(); ++i)
  1335. {
  1336. if (auto* target = targets[i])
  1337. {
  1338. if (sharedCodeGuid.isEmpty() || (addingOtherTargets != 0) == (target->type != build_tools::ProjectType::Target::StandalonePlugIn))
  1339. {
  1340. out << "Project(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"" << projectName << " - "
  1341. << target->getName() << "\", \""
  1342. << target->getVCProjFile().getFileName() << "\", \"" << target->getProjectGuid() << '"' << newLine;
  1343. if (sharedCodeGuid.isNotEmpty() && target->type != build_tools::ProjectType::Target::SharedCodeTarget)
  1344. out << "\tProjectSection(ProjectDependencies) = postProject" << newLine
  1345. << "\t\t" << sharedCodeGuid << " = " << sharedCodeGuid << newLine
  1346. << "\tEndProjectSection" << newLine;
  1347. out << "EndProject" << newLine;
  1348. }
  1349. }
  1350. }
  1351. }
  1352. }
  1353. void writeSolutionFile (OutputStream& out, const String& versionString, String commentString) const
  1354. {
  1355. const unsigned char bomBytes[] { CharPointer_UTF8::byteOrderMark1,
  1356. CharPointer_UTF8::byteOrderMark2,
  1357. CharPointer_UTF8::byteOrderMark3 };
  1358. for (const auto& byte : bomBytes)
  1359. out.writeByte ((char) byte);
  1360. if (commentString.isNotEmpty())
  1361. commentString += newLine;
  1362. out << newLine
  1363. << "Microsoft Visual Studio Solution File, Format Version " << versionString << newLine
  1364. << commentString << newLine;
  1365. writeProjectDependencies (out);
  1366. out << "Global" << newLine
  1367. << "\tGlobalSection(SolutionConfigurationPlatforms) = preSolution" << newLine;
  1368. for (ConstConfigIterator i (*this); i.next();)
  1369. {
  1370. auto& config = dynamic_cast<const MSVCBuildConfiguration&> (*i);
  1371. auto configName = config.createMSVCConfigName();
  1372. out << "\t\t" << configName << " = " << configName << newLine;
  1373. }
  1374. out << "\tEndGlobalSection" << newLine
  1375. << "\tGlobalSection(ProjectConfigurationPlatforms) = postSolution" << newLine;
  1376. for (auto& target : targets)
  1377. for (ConstConfigIterator i (*this); i.next();)
  1378. {
  1379. auto& config = dynamic_cast<const MSVCBuildConfiguration&> (*i);
  1380. auto configName = config.createMSVCConfigName();
  1381. for (auto& suffix : { "ActiveCfg", "Build.0" })
  1382. out << "\t\t" << target->getProjectGuid() << "." << configName << "." << suffix << " = " << configName << newLine;
  1383. }
  1384. out << "\tEndGlobalSection" << newLine
  1385. << "\tGlobalSection(SolutionProperties) = preSolution" << newLine
  1386. << "\t\tHideSolutionNode = FALSE" << newLine
  1387. << "\tEndGlobalSection" << newLine;
  1388. out << "EndGlobal" << newLine;
  1389. }
  1390. //==============================================================================
  1391. bool hasResourceFile() const
  1392. {
  1393. return ! projectType.isStaticLibrary();
  1394. }
  1395. void createResourcesAndIcon() const
  1396. {
  1397. if (hasResourceFile())
  1398. {
  1399. iconFile = getTargetFolder().getChildFile ("icon.ico");
  1400. build_tools::writeWinIcon (getIcons(), iconFile);
  1401. rcFile = getTargetFolder().getChildFile ("resources.rc");
  1402. createRCFile (project, iconFile, rcFile);
  1403. }
  1404. }
  1405. bool shouldAddWebView2Package() const
  1406. {
  1407. return project.getEnabledModules().isModuleEnabled ("juce_gui_extra")
  1408. && project.isConfigFlagEnabled ("JUCE_USE_WIN_WEBVIEW2", false);
  1409. }
  1410. static String getWebView2PackageName() { return "Microsoft.Web.WebView2"; }
  1411. static String getWebView2PackageVersion() { return "1.0.902.49"; }
  1412. void createPackagesConfigFile() const
  1413. {
  1414. if (shouldAddWebView2Package())
  1415. {
  1416. packagesConfigFile = getTargetFolder().getChildFile ("packages.config");
  1417. build_tools::writeStreamToFile (packagesConfigFile, [] (MemoryOutputStream& mo)
  1418. {
  1419. mo.setNewLineString ("\r\n");
  1420. mo << "<?xml version=\"1.0\" encoding=\"utf-8\"?>" << newLine
  1421. << "<packages>" << newLine
  1422. << "\t" << "<package id=" << getWebView2PackageName().quoted()
  1423. << " version=" << getWebView2PackageVersion().quoted()
  1424. << " />" << newLine
  1425. << "</packages>" << newLine;
  1426. });
  1427. }
  1428. }
  1429. static String prependDot (const String& filename)
  1430. {
  1431. return build_tools::isAbsolutePath (filename) ? filename
  1432. : (".\\" + filename);
  1433. }
  1434. static bool shouldUseStdCall (const build_tools::RelativePath& path)
  1435. {
  1436. return path.getFileNameWithoutExtension().startsWithIgnoreCase ("include_juce_audio_plugin_client_RTAS_");
  1437. }
  1438. static bool shouldAddBigobjFlag (const build_tools::RelativePath& path)
  1439. {
  1440. return path.getFileNameWithoutExtension().equalsIgnoreCase ("include_juce_gui_basics");
  1441. }
  1442. StringArray getModuleLibs() const
  1443. {
  1444. StringArray result;
  1445. for (auto& lib : windowsLibs)
  1446. result.add (lib + ".lib");
  1447. return result;
  1448. }
  1449. JUCE_DECLARE_NON_COPYABLE (MSVCProjectExporterBase)
  1450. };
  1451. //==============================================================================
  1452. class MSVCProjectExporterVC2015 : public MSVCProjectExporterBase
  1453. {
  1454. public:
  1455. MSVCProjectExporterVC2015 (Project& p, const ValueTree& t)
  1456. : MSVCProjectExporterBase (p, t, getTargetFolderName())
  1457. {
  1458. name = getDisplayName();
  1459. targetPlatformVersion.setDefault (getDefaultWindowsTargetPlatformVersion());
  1460. platformToolsetValue.setDefault (getDefaultToolset());
  1461. }
  1462. static String getDisplayName() { return "Visual Studio 2015"; }
  1463. static String getValueTreeTypeName() { return "VS2015"; }
  1464. static String getTargetFolderName() { return "VisualStudio2015"; }
  1465. Identifier getExporterIdentifier() const override { return getValueTreeTypeName(); }
  1466. int getVisualStudioVersion() const override { return 14; }
  1467. String getSolutionComment() const override { return "# Visual Studio 14"; }
  1468. String getToolsVersion() const override { return "14.0"; }
  1469. String getDefaultToolset() const override { return "v140"; }
  1470. String getDefaultWindowsTargetPlatformVersion() const override { return "8.1"; }
  1471. static MSVCProjectExporterVC2015* createForSettings (Project& projectToUse, const ValueTree& settingsToUse)
  1472. {
  1473. if (settingsToUse.hasType (getValueTreeTypeName()))
  1474. return new MSVCProjectExporterVC2015 (projectToUse, settingsToUse);
  1475. return nullptr;
  1476. }
  1477. void createExporterProperties (PropertyListBuilder& props) override
  1478. {
  1479. static const char* toolsetNames[] = { "v140", "v140_xp", "CTP_Nov2013" };
  1480. const var toolsets[] = { "v140", "v140_xp", "CTP_Nov2013" };
  1481. addToolsetProperty (props, toolsetNames, toolsets, numElementsInArray (toolsets));
  1482. MSVCProjectExporterBase::createExporterProperties (props);
  1483. }
  1484. JUCE_DECLARE_NON_COPYABLE (MSVCProjectExporterVC2015)
  1485. };
  1486. //==============================================================================
  1487. class MSVCProjectExporterVC2017 : public MSVCProjectExporterBase
  1488. {
  1489. public:
  1490. MSVCProjectExporterVC2017 (Project& p, const ValueTree& t)
  1491. : MSVCProjectExporterBase (p, t, getTargetFolderName())
  1492. {
  1493. name = getDisplayName();
  1494. targetPlatformVersion.setDefault (getDefaultWindowsTargetPlatformVersion());
  1495. platformToolsetValue.setDefault (getDefaultToolset());
  1496. }
  1497. static String getDisplayName() { return "Visual Studio 2017"; }
  1498. static String getValueTreeTypeName() { return "VS2017"; }
  1499. static String getTargetFolderName() { return "VisualStudio2017"; }
  1500. Identifier getExporterIdentifier() const override { return getValueTreeTypeName(); }
  1501. int getVisualStudioVersion() const override { return 15; }
  1502. String getSolutionComment() const override { return "# Visual Studio 15"; }
  1503. String getToolsVersion() const override { return "15.0"; }
  1504. String getDefaultToolset() const override { return "v141"; }
  1505. String getDefaultWindowsTargetPlatformVersion() const override { return "Latest"; }
  1506. static MSVCProjectExporterVC2017* createForSettings (Project& projectToUse, const ValueTree& settingsToUse)
  1507. {
  1508. if (settingsToUse.hasType (getValueTreeTypeName()))
  1509. return new MSVCProjectExporterVC2017 (projectToUse, settingsToUse);
  1510. return nullptr;
  1511. }
  1512. void createExporterProperties (PropertyListBuilder& props) override
  1513. {
  1514. static const char* toolsetNames[] = { "v140", "v140_xp", "v141", "v141_xp" };
  1515. const var toolsets[] = { "v140", "v140_xp", "v141", "v141_xp" };
  1516. addToolsetProperty (props, toolsetNames, toolsets, numElementsInArray (toolsets));
  1517. MSVCProjectExporterBase::createExporterProperties (props);
  1518. }
  1519. JUCE_DECLARE_NON_COPYABLE (MSVCProjectExporterVC2017)
  1520. };
  1521. //==============================================================================
  1522. class MSVCProjectExporterVC2019 : public MSVCProjectExporterBase
  1523. {
  1524. public:
  1525. MSVCProjectExporterVC2019 (Project& p, const ValueTree& t)
  1526. : MSVCProjectExporterBase (p, t, getTargetFolderName())
  1527. {
  1528. name = getDisplayName();
  1529. targetPlatformVersion.setDefault (getDefaultWindowsTargetPlatformVersion());
  1530. platformToolsetValue.setDefault (getDefaultToolset());
  1531. }
  1532. static String getDisplayName() { return "Visual Studio 2019"; }
  1533. static String getValueTreeTypeName() { return "VS2019"; }
  1534. static String getTargetFolderName() { return "VisualStudio2019"; }
  1535. Identifier getExporterIdentifier() const override { return getValueTreeTypeName(); }
  1536. int getVisualStudioVersion() const override { return 16; }
  1537. String getSolutionComment() const override { return "# Visual Studio Version 16"; }
  1538. String getToolsVersion() const override { return "16.0"; }
  1539. String getDefaultToolset() const override { return "v142"; }
  1540. String getDefaultWindowsTargetPlatformVersion() const override { return "10.0"; }
  1541. static MSVCProjectExporterVC2019* createForSettings (Project& projectToUse, const ValueTree& settingsToUse)
  1542. {
  1543. if (settingsToUse.hasType (getValueTreeTypeName()))
  1544. return new MSVCProjectExporterVC2019 (projectToUse, settingsToUse);
  1545. return nullptr;
  1546. }
  1547. void createExporterProperties (PropertyListBuilder& props) override
  1548. {
  1549. static const char* toolsetNames[] = { "v140", "v140_xp", "v141", "v141_xp", "v142" };
  1550. const var toolsets[] = { "v140", "v140_xp", "v141", "v141_xp", "v142" };
  1551. addToolsetProperty (props, toolsetNames, toolsets, numElementsInArray (toolsets));
  1552. MSVCProjectExporterBase::createExporterProperties (props);
  1553. }
  1554. JUCE_DECLARE_NON_COPYABLE (MSVCProjectExporterVC2019)
  1555. };
  1556. //==============================================================================
  1557. class MSVCProjectExporterVC2022 : public MSVCProjectExporterBase
  1558. {
  1559. public:
  1560. MSVCProjectExporterVC2022 (Project& p, const ValueTree& t)
  1561. : MSVCProjectExporterBase (p, t, getTargetFolderName())
  1562. {
  1563. name = getDisplayName();
  1564. targetPlatformVersion.setDefault (getDefaultWindowsTargetPlatformVersion());
  1565. platformToolsetValue.setDefault (getDefaultToolset());
  1566. }
  1567. static String getDisplayName() { return "Visual Studio 2022"; }
  1568. static String getValueTreeTypeName() { return "VS2022"; }
  1569. static String getTargetFolderName() { return "VisualStudio2022"; }
  1570. Identifier getExporterIdentifier() const override { return getValueTreeTypeName(); }
  1571. int getVisualStudioVersion() const override { return 17; }
  1572. String getSolutionComment() const override { return "# Visual Studio Version 17"; }
  1573. String getToolsVersion() const override { return "17.0"; }
  1574. String getDefaultToolset() const override { return "v143"; }
  1575. String getDefaultWindowsTargetPlatformVersion() const override { return "10.0"; }
  1576. static MSVCProjectExporterVC2022* createForSettings (Project& projectToUse, const ValueTree& settingsToUse)
  1577. {
  1578. if (settingsToUse.hasType (getValueTreeTypeName()))
  1579. return new MSVCProjectExporterVC2022 (projectToUse, settingsToUse);
  1580. return nullptr;
  1581. }
  1582. void createExporterProperties (PropertyListBuilder& props) override
  1583. {
  1584. static const char* toolsetNames[] = { "v140", "v140_xp", "v141", "v141_xp", "v142", "v143" };
  1585. const var toolsets[] = { "v140", "v140_xp", "v141", "v141_xp", "v142", "v143" };
  1586. addToolsetProperty (props, toolsetNames, toolsets, numElementsInArray (toolsets));
  1587. MSVCProjectExporterBase::createExporterProperties (props);
  1588. }
  1589. JUCE_DECLARE_NON_COPYABLE (MSVCProjectExporterVC2022)
  1590. };