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.

2064 lines
101KB

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