contrib/buildsystems: add a backend for modern Visual Studio versions

Based on the previous patches in this patch series that fixed the generator for `.vcproj` files (which were used by Visual Studio prior to 2015 to define projects), this patch offers to generate project definitions for neweer versions of Visual Studio (which use `.vcxproj` files). To that end, this patch copy-edits the generator of the `.vcproj`. In addition, we now use the `vcpkg` system which allows us to build Git's dependencies (e.g. curl, libexpat) conveniently. The support scripts were introduced in the `jh/msvc` patch series, and with this patch we initialize the `vcpkg` conditionally, in the `libgit` project's `PreBuildEvent`. To allow for parallel building of the projects, we therefore put `libgit` at the bottom of the project hierarchy. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Johannes Schindelin committed Jul 29, 2019 at 13:08 UTC 384a61bc6a8e6dc241d685acf4fb7b5a15003dbb
1 file changed +385
contrib/buildsystems/Generators/Vcxproj.pm new
+385
@@ -0,0 +1,385 @@
1 +package Generators::Vcxproj;
2 +require Exporter;
3 +
4 +use strict;
5 +use vars qw($VERSION);
6 +use Digest::SHA qw(sha256_hex);
7 +
8 +our $VERSION = '1.00';
9 +our(@ISA, @EXPORT, @EXPORT_OK, @AVAILABLE);
10 +@ISA = qw(Exporter);
11 +
12 +BEGIN {
13 + push @EXPORT_OK, qw(generate);
14 +}
15 +
16 +sub generate_guid ($) {
17 + my $hex = sha256_hex($_[0]);
18 + $hex =~ s/^(.{8})(.{4})(.{4})(.{4})(.{12}).*/{$1-$2-$3-$4-$5}/;
19 + $hex =~ tr/a-z/A-Z/;
20 + return $hex;
21 +}
22 +
23 +sub generate {
24 + my ($git_dir, $out_dir, $rel_dir, %build_structure) = @_;
25 + my @libs = @{$build_structure{"LIBS"}};
26 + foreach (@libs) {
27 + createProject($_, $git_dir, $out_dir, $rel_dir, \%build_structure, 1);
28 + }
29 +
30 + my @apps = @{$build_structure{"APPS"}};
31 + foreach (@apps) {
32 + createProject($_, $git_dir, $out_dir, $rel_dir, \%build_structure, 0);
33 + }
34 +
35 + createGlueProject($git_dir, $out_dir, $rel_dir, %build_structure);
36 + return 0;
37 +}
38 +
39 +sub createProject {
40 + my ($name, $git_dir, $out_dir, $rel_dir, $build_structure, $static_library) = @_;
41 + my $label = $static_library ? "lib" : "app";
42 + my $prefix = $static_library ? "LIBS_" : "APPS_";
43 + my $config_type = $static_library ? "StaticLibrary" : "Application";
44 + print "Generate $name vcxproj $label project\n";
45 + my $cdup = $name;
46 + $cdup =~ s/[^\/]+/../g;
47 + $cdup =~ s/\//\\/g;
48 + $rel_dir = $rel_dir eq "." ? $cdup : "$cdup\\$rel_dir";
49 + $rel_dir =~ s/\//\\/g;
50 +
51 + my $target = $name;
52 + if ($static_library) {
53 + $target =~ s/\.a//;
54 + } else {
55 + $target =~ s/\.exe//;
56 + }
57 +
58 + my $uuid = generate_guid($name);
59 + $$build_structure{"$prefix${target}_GUID"} = $uuid;
60 + my $vcxproj = $target;
61 + $vcxproj =~ s/(.*\/)?(.*)/$&\/$2.vcxproj/;
62 + $vcxproj =~ s/([^\/]*)(\/lib)\/(lib.vcxproj)/$1$2\/$1_$3/;
63 + $$build_structure{"$prefix${target}_VCXPROJ"} = $vcxproj;
64 +
65 + my @srcs = sort(map("$rel_dir\\$_", @{$$build_structure{"$prefix${name}_SOURCES"}}));
66 + my @sources;
67 + foreach (@srcs) {
68 + $_ =~ s/\//\\/g;
69 + push(@sources, $_);
70 + }
71 + my $defines = join(";", sort(@{$$build_structure{"$prefix${name}_DEFINES"}}));
72 + my $includes= join(";", sort(map { s/^-I//; s/\//\\/g; File::Spec->file_name_is_absolute($_) ? $_ : "$rel_dir\\$_" } @{$$build_structure{"$prefix${name}_INCLUDES"}}));
73 + my $cflags = join(" ", sort(map { s/^-[GLMOWZ].*//; s/.* .*/"$&"/; $_; } @{$$build_structure{"$prefix${name}_CFLAGS"}}));
74 + $cflags =~ s/</&lt;/g;
75 + $cflags =~ s/>/&gt;/g;
76 +
77 + my $libs_release = "\n ";
78 + my $libs_debug = "\n ";
79 + if (!$static_library) {
80 + $libs_release = join(";", sort(grep /^(?!libgit\.lib|xdiff\/lib\.lib|vcs-svn\/lib\.lib)/, @{$$build_structure{"$prefix${name}_LIBS"}}));
81 + $libs_debug = $libs_release;
82 + $libs_debug =~ s/zlib\.lib/zlibd\.lib/;
83 + }
84 +
85 + $defines =~ s/-D//g;
86 + $defines =~ s/</&lt;/g;
87 + $defines =~ s/>/&gt;/g;
88 + $defines =~ s/\'//g;
89 +
90 + die "Could not create the directory $target for $label project!\n" unless (-d "$target" || mkdir "$target");
91 +
92 + open F, ">$vcxproj" or die "Could not open $vcxproj for writing!\n";
93 + binmode F, ":crlf :utf8";
94 + print F chr(0xFEFF);
95 + print F << "EOM";
96 +<?xml version="1.0" encoding="utf-8"?>
97 +<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
98 + <ItemGroup Label="ProjectConfigurations">
99 + <ProjectConfiguration Include="Debug|Win32">
100 + <Configuration>Debug</Configuration>
101 + <Platform>Win32</Platform>
102 + </ProjectConfiguration>
103 + <ProjectConfiguration Include="Release|Win32">
104 + <Configuration>Release</Configuration>
105 + <Platform>Win32</Platform>
106 + </ProjectConfiguration>
107 + <ProjectConfiguration Include="Debug|x64">
108 + <Configuration>Debug</Configuration>
109 + <Platform>x64</Platform>
110 + </ProjectConfiguration>
111 + <ProjectConfiguration Include="Release|x64">
112 + <Configuration>Release</Configuration>
113 + <Platform>x64</Platform>
114 + </ProjectConfiguration>
115 + </ItemGroup>
116 + <PropertyGroup Label="Globals">
117 + <ProjectGuid>$uuid</ProjectGuid>
118 + <Keyword>Win32Proj</Keyword>
119 + <VCPKGArch Condition="'\$(Platform)'=='Win32'">x86-windows</VCPKGArch>
120 + <VCPKGArch Condition="'\$(Platform)'!='Win32'">x64-windows</VCPKGArch>
121 + <VCPKGArchDirectory>$cdup\\compat\\vcbuild\\vcpkg\\installed\\\$(VCPKGArch)</VCPKGArchDirectory>
122 + <VCPKGBinDirectory Condition="'\(Configuration)'=='Debug'">\$(VCPKGArchDirectory)\\debug\\bin</VCPKGBinDirectory>
123 + <VCPKGLibDirectory Condition="'\(Configuration)'=='Debug'">\$(VCPKGArchDirectory)\\debug\\lib</VCPKGLibDirectory>
124 + <VCPKGBinDirectory Condition="'\(Configuration)'!='Debug'">\$(VCPKGArchDirectory)\\bin</VCPKGBinDirectory>
125 + <VCPKGLibDirectory Condition="'\(Configuration)'!='Debug'">\$(VCPKGArchDirectory)\\lib</VCPKGLibDirectory>
126 + <VCPKGIncludeDirectory>\$(VCPKGArchDirectory)\\include</VCPKGIncludeDirectory>
127 + <VCPKGLibs Condition="'\(Configuration)'=='Debug'">$libs_debug</VCPKGLibs>
128 + <VCPKGLibs Condition="'\(Configuration)'!='Debug'">$libs_release</VCPKGLibs>
129 + </PropertyGroup>
130 + <Import Project="\$(VCTargetsPath)\\Microsoft.Cpp.Default.props" />
131 + <PropertyGroup Condition="'\$(Configuration)'=='Debug'" Label="Configuration">
132 + <UseDebugLibraries>true</UseDebugLibraries>
133 + <LinkIncremental>true</LinkIncremental>
134 + </PropertyGroup>
135 + <PropertyGroup Condition="'\$(Configuration)'=='Release'" Label="Configuration">
136 + <UseDebugLibraries>false</UseDebugLibraries>
137 + <WholeProgramOptimization>true</WholeProgramOptimization>
138 + </PropertyGroup>
139 + <PropertyGroup>
140 + <ConfigurationType>$config_type</ConfigurationType>
141 + <PlatformToolset>v140</PlatformToolset>
142 + <!-- <CharacterSet>UTF-8</CharacterSet> -->
143 + <OutDir>..\\</OutDir>
144 + <!-- <IntDir>\$(ProjectDir)\$(Configuration)\\</IntDir> -->
145 + </PropertyGroup>
146 + <Import Project="\$(VCTargetsPath)\\Microsoft.Cpp.props" />
147 + <ImportGroup Label="ExtensionSettings">
148 + </ImportGroup>
149 + <ImportGroup Label="Shared">
150 + </ImportGroup>
151 + <ImportGroup Label="PropertySheets">
152 + <Import Project="\$(UserRootDir)\\Microsoft.Cpp.\$(Platform).user.props" Condition="exists('\$(UserRootDir)\\Microsoft.Cpp.\$(Platform).user.props')" Label="LocalAppDataPlatform" />
153 + </ImportGroup>
154 + <PropertyGroup Label="UserMacros" />
155 + <PropertyGroup>
156 + <GenerateManifest>false</GenerateManifest>
157 + <EnableManagedIncrementalBuild>true</EnableManagedIncrementalBuild>
158 + </PropertyGroup>
159 + <ItemDefinitionGroup>
160 + <ClCompile>
161 + <AdditionalOptions>$cflags %(AdditionalOptions)</AdditionalOptions>
162 + <AdditionalIncludeDirectories>$cdup;$cdup\\compat;$cdup\\compat\\regex;$cdup\\compat\\win32;$cdup\\compat\\poll;$cdup\\compat\\vcbuild\\include;\$(VCPKGIncludeDirectory);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
163 + <EnableParallelCodeGeneration />
164 + <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
165 + <PrecompiledHeader />
166 + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
167 + </ClCompile>
168 + <Lib>
169 + <SuppressStartupBanner>true</SuppressStartupBanner>
170 + </Lib>
171 + <Link>
172 + <AdditionalLibraryDirectories>\$(VCPKGLibDirectory);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
173 + <AdditionalDependencies>\$(VCPKGLibs);\$(AdditionalDependencies)</AdditionalDependencies>
174 + <AdditionalOptions>invalidcontinue.obj %(AdditionalOptions)</AdditionalOptions>
175 + <EntryPointSymbol>wmainCRTStartup</EntryPointSymbol>
176 + <ManifestFile>$cdup\\compat\\win32\\git.manifest</ManifestFile>
177 + <SubSystem>Console</SubSystem>
178 + </Link>
179 +EOM
180 + if ($target eq 'libgit') {
181 + print F << "EOM";
182 + <PreBuildEvent Condition="!Exists('$cdup\\compat\\vcbuild\\vcpkg\\installed\\\$(VCPKGArch)\\include\\openssl\\ssl.h')">
183 + <Message>Initialize VCPKG</Message>
184 + <Command>del "$cdup\\compat\\vcbuild\\vcpkg"</Command>
185 + <Command>call "$cdup\\compat\\vcbuild\\vcpkg_install.bat"</Command>
186 + </PreBuildEvent>
187 +EOM
188 + }
189 + print F << "EOM";
190 + </ItemDefinitionGroup>
191 + <ItemDefinitionGroup Condition="'\$(Platform)'=='Win32'">
192 + <Link>
193 + <TargetMachine>MachineX86</TargetMachine>
194 + </Link>
195 + </ItemDefinitionGroup>
196 + <ItemDefinitionGroup Condition="'\$(Configuration)'=='Debug'">
197 + <ClCompile>
198 + <Optimization>Disabled</Optimization>
199 + <PreprocessorDefinitions>WIN32;_DEBUG;$defines;%(PreprocessorDefinitions)</PreprocessorDefinitions>
200 + <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
201 + </ClCompile>
202 + <Link>
203 + <GenerateDebugInformation>true</GenerateDebugInformation>
204 + </Link>
205 + </ItemDefinitionGroup>
206 + <ItemDefinitionGroup Condition="'\$(Configuration)'=='Release'">
207 + <ClCompile>
208 + <Optimization>MaxSpeed</Optimization>
209 + <IntrinsicFunctions>true</IntrinsicFunctions>
210 + <PreprocessorDefinitions>WIN32;NDEBUG;$defines;%(PreprocessorDefinitions)</PreprocessorDefinitions>
211 + <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
212 + <FunctionLevelLinking>true</FunctionLevelLinking>
213 + <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
214 + </ClCompile>
215 + <Link>
216 + <GenerateDebugInformation>true</GenerateDebugInformation>
217 + <EnableCOMDATFolding>true</EnableCOMDATFolding>
218 + <OptimizeReferences>true</OptimizeReferences>
219 + </Link>
220 + </ItemDefinitionGroup>
221 + <ItemGroup>
222 +EOM
223 + foreach(@sources) {
224 + print F << "EOM";
225 + <ClCompile Include="$_" />
226 +EOM
227 + }
228 + print F << "EOM";
229 + </ItemGroup>
230 +EOM
231 + if (!$static_library || $target =~ 'vcs-svn' || $target =~ 'xdiff') {
232 + my $uuid_libgit = $$build_structure{"LIBS_libgit_GUID"};
233 + my $uuid_xdiff_lib = $$build_structure{"LIBS_xdiff/lib_GUID"};
234 +
235 + print F << "EOM";
236 + <ItemGroup>
237 + <ProjectReference Include="$cdup\\libgit\\libgit.vcxproj">
238 + <Project>$uuid_libgit</Project>
239 + <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
240 + </ProjectReference>
241 +EOM
242 + if (!($name =~ 'xdiff')) {
243 + print F << "EOM";
244 + <ProjectReference Include="$cdup\\xdiff\\lib\\xdiff_lib.vcxproj">
245 + <Project>$uuid_xdiff_lib</Project>
246 + <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
247 + </ProjectReference>
248 +EOM
249 + }
250 + if ($name =~ /(test-(line-buffer|svn-fe)|^git-remote-testsvn)\.exe$/) {
251 + my $uuid_vcs_svn_lib = $$build_structure{"LIBS_vcs-svn/lib_GUID"};
252 + print F << "EOM";
253 + <ProjectReference Include="$cdup\\vcs-svn\\lib\\vcs-svn_lib.vcxproj">
254 + <Project>$uuid_vcs_svn_lib</Project>
255 + <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
256 + </ProjectReference>
257 +EOM
258 + }
259 + print F << "EOM";
260 + </ItemGroup>
261 +EOM
262 + }
263 + print F << "EOM";
264 + <Import Project="\$(VCTargetsPath)\\Microsoft.Cpp.targets" />
265 +EOM
266 + if (!$static_library) {
267 + print F << "EOM";
268 + <Target Name="${target}_AfterBuild" AfterTargets="AfterBuild">
269 + <ItemGroup>
270 + <DLLsAndPDBs Include="\$(VCPKGBinDirectory)\\*.dll;\$(VCPKGBinDirectory)\\*.pdb" />
271 + </ItemGroup>
272 + <Copy SourceFiles="@(DLLsAndPDBs)" DestinationFolder="\$(OutDir)" SkipUnchangedFiles="true" UseHardlinksIfPossible="true" />
273 + <MakeDir Directories="..\\templates\\blt\\branches" />
274 + </Target>
275 +EOM
276 + }
277 + print F << "EOM";
278 +</Project>
279 +EOM
280 + close F;
281 +}
282 +
283 +sub createGlueProject {
284 + my ($git_dir, $out_dir, $rel_dir, %build_structure) = @_;
285 + print "Generate solutions file\n";
286 + $rel_dir = "..\\$rel_dir";
287 + $rel_dir =~ s/\//\\/g;
288 + my $SLN_HEAD = "Microsoft Visual Studio Solution File, Format Version 11.00\n# Visual Studio 2010\n";
289 + my $SLN_PRE = "Project(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = ";
290 + my $SLN_POST = "\nEndProject\n";
291 +
292 + my @libs = @{$build_structure{"LIBS"}};
293 + my @tmp;
294 + foreach (@libs) {
295 + $_ =~ s/\.a//;
296 + push(@tmp, $_);
297 + }
298 + @libs = @tmp;
299 +
300 + my @apps = @{$build_structure{"APPS"}};
301 + @tmp = ();
302 + foreach (@apps) {
303 + $_ =~ s/\.exe//;
304 + if ($_ eq "git" ) {
305 + unshift(@tmp, $_);
306 + } else {
307 + push(@tmp, $_);
308 + }
309 + }
310 + @apps = @tmp;
311 +
312 + open F, ">git.sln" || die "Could not open git.sln for writing!\n";
313 + binmode F, ":crlf :utf8";
314 + print F chr(0xFEFF);
315 + print F "$SLN_HEAD";
316 +
317 + foreach (@apps) {
318 + my $appname = $_;
319 + my $uuid = $build_structure{"APPS_${appname}_GUID"};
320 + print F "$SLN_PRE";
321 + my $vcxproj = $build_structure{"APPS_${appname}_VCXPROJ"};
322 + $vcxproj =~ s/\//\\/g;
323 + $appname =~ s/.*\///;
324 + print F "\"${appname}\", \"${vcxproj}\", \"${uuid}\"";
325 + print F "$SLN_POST";
326 + }
327 + foreach (@libs) {
328 + my $libname = $_;
329 + my $uuid = $build_structure{"LIBS_${libname}_GUID"};
330 + print F "$SLN_PRE";
331 + my $vcxproj = $build_structure{"LIBS_${libname}_VCXPROJ"};
332 + $vcxproj =~ s/\//\\/g;
333 + $libname =~ s/\//_/g;
334 + print F "\"${libname}\", \"${vcxproj}\", \"${uuid}\"";
335 + print F "$SLN_POST";
336 + }
337 +
338 + print F << "EOM";
339 +Global
340 + GlobalSection(SolutionConfigurationPlatforms) = preSolution
341 + Debug|x64 = Debug|x64
342 + Debug|x86 = Debug|x86
343 + Release|x64 = Release|x64
344 + Release|x86 = Release|x86
345 + EndGlobalSection
346 +EOM
347 + print F << "EOM";
348 + GlobalSection(ProjectConfigurationPlatforms) = postSolution
349 +EOM
350 + foreach (@apps) {
351 + my $appname = $_;
352 + my $uuid = $build_structure{"APPS_${appname}_GUID"};
353 + print F "\t\t${uuid}.Debug|x64.ActiveCfg = Debug|x64\n";
354 + print F "\t\t${uuid}.Debug|x64.Build.0 = Debug|x64\n";
355 + print F "\t\t${uuid}.Debug|x86.ActiveCfg = Debug|Win32\n";
356 + print F "\t\t${uuid}.Debug|x86.Build.0 = Debug|Win32\n";
357 + print F "\t\t${uuid}.Release|x64.ActiveCfg = Release|x64\n";
358 + print F "\t\t${uuid}.Release|x64.Build.0 = Release|x64\n";
359 + print F "\t\t${uuid}.Release|x86.ActiveCfg = Release|Win32\n";
360 + print F "\t\t${uuid}.Release|x86.Build.0 = Release|Win32\n";
361 + }
362 + foreach (@libs) {
363 + my $libname = $_;
364 + my $uuid = $build_structure{"LIBS_${libname}_GUID"};
365 + print F "\t\t${uuid}.Debug|x64.ActiveCfg = Debug|x64\n";
366 + print F "\t\t${uuid}.Debug|x64.Build.0 = Debug|x64\n";
367 + print F "\t\t${uuid}.Debug|x86.ActiveCfg = Debug|Win32\n";
368 + print F "\t\t${uuid}.Debug|x86.Build.0 = Debug|Win32\n";
369 + print F "\t\t${uuid}.Release|x64.ActiveCfg = Release|x64\n";
370 + print F "\t\t${uuid}.Release|x64.Build.0 = Release|x64\n";
371 + print F "\t\t${uuid}.Release|x86.ActiveCfg = Release|Win32\n";
372 + print F "\t\t${uuid}.Release|x86.Build.0 = Release|Win32\n";
373 + }
374 +
375 + print F << "EOM";
376 + EndGlobalSection
377 + GlobalSection(SolutionProperties) = preSolution
378 + HideSolutionNode = FALSE
379 + EndGlobalSection
380 +EndGlobal
381 +EOM
382 + close F;
383 +}
384 +
385 +1;