contrib/buildsystems: add a backend for modern Visual Studio versions
[git/git.git] / contrib / buildsystems / Generators / Vcxproj.pm
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;