Merge branch 'js/azure-pipelines-msvc'
[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/g;
83       $libs_debug =~ s/libcurl\.lib/libcurl-d\.lib/g;
84     }
85
86     $defines =~ s/-D//g;
87     $defines =~ s/</&lt;/g;
88     $defines =~ s/>/&gt;/g;
89     $defines =~ s/\'//g;
90
91     die "Could not create the directory $target for $label project!\n" unless (-d "$target" || mkdir "$target");
92
93     open F, ">$vcxproj" or die "Could not open $vcxproj for writing!\n";
94     binmode F, ":crlf :utf8";
95     print F chr(0xFEFF);
96     print F << "EOM";
97 <?xml version="1.0" encoding="utf-8"?>
98 <Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
99   <ItemGroup Label="ProjectConfigurations">
100     <ProjectConfiguration Include="Debug|Win32">
101       <Configuration>Debug</Configuration>
102       <Platform>Win32</Platform>
103     </ProjectConfiguration>
104     <ProjectConfiguration Include="Release|Win32">
105       <Configuration>Release</Configuration>
106       <Platform>Win32</Platform>
107     </ProjectConfiguration>
108     <ProjectConfiguration Include="Debug|x64">
109       <Configuration>Debug</Configuration>
110       <Platform>x64</Platform>
111     </ProjectConfiguration>
112     <ProjectConfiguration Include="Release|x64">
113       <Configuration>Release</Configuration>
114       <Platform>x64</Platform>
115     </ProjectConfiguration>
116   </ItemGroup>
117   <PropertyGroup Label="Globals">
118     <ProjectGuid>$uuid</ProjectGuid>
119     <Keyword>Win32Proj</Keyword>
120     <VCPKGArch Condition="'\$(Platform)'=='Win32'">x86-windows</VCPKGArch>
121     <VCPKGArch Condition="'\$(Platform)'!='Win32'">x64-windows</VCPKGArch>
122     <VCPKGArchDirectory>$cdup\\compat\\vcbuild\\vcpkg\\installed\\\$(VCPKGArch)</VCPKGArchDirectory>
123     <VCPKGBinDirectory Condition="'\$(Configuration)'=='Debug'">\$(VCPKGArchDirectory)\\debug\\bin</VCPKGBinDirectory>
124     <VCPKGLibDirectory Condition="'\$(Configuration)'=='Debug'">\$(VCPKGArchDirectory)\\debug\\lib</VCPKGLibDirectory>
125     <VCPKGBinDirectory Condition="'\$(Configuration)'!='Debug'">\$(VCPKGArchDirectory)\\bin</VCPKGBinDirectory>
126     <VCPKGLibDirectory Condition="'\$(Configuration)'!='Debug'">\$(VCPKGArchDirectory)\\lib</VCPKGLibDirectory>
127     <VCPKGIncludeDirectory>\$(VCPKGArchDirectory)\\include</VCPKGIncludeDirectory>
128     <VCPKGLibs Condition="'\$(Configuration)'=='Debug'">$libs_debug</VCPKGLibs>
129     <VCPKGLibs Condition="'\$(Configuration)'!='Debug'">$libs_release</VCPKGLibs>
130   </PropertyGroup>
131   <Import Project="\$(VCTargetsPath)\\Microsoft.Cpp.Default.props" />
132   <PropertyGroup Condition="'\$(Configuration)'=='Debug'" Label="Configuration">
133     <UseDebugLibraries>true</UseDebugLibraries>
134     <LinkIncremental>true</LinkIncremental>
135   </PropertyGroup>
136   <PropertyGroup Condition="'\$(Configuration)'=='Release'" Label="Configuration">
137     <UseDebugLibraries>false</UseDebugLibraries>
138     <WholeProgramOptimization>true</WholeProgramOptimization>
139   </PropertyGroup>
140   <PropertyGroup>
141     <ConfigurationType>$config_type</ConfigurationType>
142     <PlatformToolset>v140</PlatformToolset>
143     <!-- <CharacterSet>UTF-8</CharacterSet> -->
144     <OutDir>..\\</OutDir>
145     <!-- <IntDir>\$(ProjectDir)\$(Configuration)\\</IntDir> -->
146   </PropertyGroup>
147   <Import Project="\$(VCTargetsPath)\\Microsoft.Cpp.props" />
148   <ImportGroup Label="ExtensionSettings">
149   </ImportGroup>
150   <ImportGroup Label="Shared">
151   </ImportGroup>
152   <ImportGroup Label="PropertySheets">
153     <Import Project="\$(UserRootDir)\\Microsoft.Cpp.\$(Platform).user.props" Condition="exists('\$(UserRootDir)\\Microsoft.Cpp.\$(Platform).user.props')" Label="LocalAppDataPlatform" />
154   </ImportGroup>
155   <PropertyGroup Label="UserMacros" />
156   <PropertyGroup>
157     <GenerateManifest>false</GenerateManifest>
158     <EnableManagedIncrementalBuild>true</EnableManagedIncrementalBuild>
159   </PropertyGroup>
160   <ItemDefinitionGroup>
161     <ClCompile>
162       <AdditionalOptions>$cflags %(AdditionalOptions)</AdditionalOptions>
163       <AdditionalIncludeDirectories>$cdup;$cdup\\compat;$cdup\\compat\\regex;$cdup\\compat\\win32;$cdup\\compat\\poll;$cdup\\compat\\vcbuild\\include;\$(VCPKGIncludeDirectory);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
164       <EnableParallelCodeGeneration />
165       <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
166       <PrecompiledHeader />
167       <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
168     </ClCompile>
169     <Lib>
170       <SuppressStartupBanner>true</SuppressStartupBanner>
171     </Lib>
172     <Link>
173       <AdditionalLibraryDirectories>\$(VCPKGLibDirectory);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
174       <AdditionalDependencies>\$(VCPKGLibs);\$(AdditionalDependencies)</AdditionalDependencies>
175       <AdditionalOptions>invalidcontinue.obj %(AdditionalOptions)</AdditionalOptions>
176       <EntryPointSymbol>wmainCRTStartup</EntryPointSymbol>
177       <ManifestFile>$cdup\\compat\\win32\\git.manifest</ManifestFile>
178       <SubSystem>Console</SubSystem>
179     </Link>
180 EOM
181     if ($target eq 'libgit') {
182         print F << "EOM";
183     <PreBuildEvent Condition="!Exists('$cdup\\compat\\vcbuild\\vcpkg\\installed\\\$(VCPKGArch)\\include\\openssl\\ssl.h')">
184       <Message>Initialize VCPKG</Message>
185       <Command>del "$cdup\\compat\\vcbuild\\vcpkg"</Command>
186       <Command>call "$cdup\\compat\\vcbuild\\vcpkg_install.bat"</Command>
187     </PreBuildEvent>
188 EOM
189     }
190     print F << "EOM";
191   </ItemDefinitionGroup>
192   <ItemDefinitionGroup Condition="'\$(Platform)'=='Win32'">
193     <Link>
194       <TargetMachine>MachineX86</TargetMachine>
195     </Link>
196   </ItemDefinitionGroup>
197   <ItemDefinitionGroup Condition="'\$(Configuration)'=='Debug'">
198     <ClCompile>
199       <Optimization>Disabled</Optimization>
200       <PreprocessorDefinitions>WIN32;_DEBUG;$defines;%(PreprocessorDefinitions)</PreprocessorDefinitions>
201       <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
202     </ClCompile>
203     <Link>
204       <GenerateDebugInformation>true</GenerateDebugInformation>
205     </Link>
206   </ItemDefinitionGroup>
207   <ItemDefinitionGroup Condition="'\$(Configuration)'=='Release'">
208     <ClCompile>
209       <Optimization>MaxSpeed</Optimization>
210       <IntrinsicFunctions>true</IntrinsicFunctions>
211       <PreprocessorDefinitions>WIN32;NDEBUG;$defines;%(PreprocessorDefinitions)</PreprocessorDefinitions>
212       <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
213       <FunctionLevelLinking>true</FunctionLevelLinking>
214       <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
215     </ClCompile>
216     <Link>
217       <GenerateDebugInformation>true</GenerateDebugInformation>
218       <EnableCOMDATFolding>true</EnableCOMDATFolding>
219       <OptimizeReferences>true</OptimizeReferences>
220     </Link>
221   </ItemDefinitionGroup>
222   <ItemGroup>
223 EOM
224     foreach(@sources) {
225         print F << "EOM";
226     <ClCompile Include="$_" />
227 EOM
228     }
229     print F << "EOM";
230   </ItemGroup>
231 EOM
232     if (!$static_library || $target =~ 'vcs-svn' || $target =~ 'xdiff') {
233       my $uuid_libgit = $$build_structure{"LIBS_libgit_GUID"};
234       my $uuid_xdiff_lib = $$build_structure{"LIBS_xdiff/lib_GUID"};
235
236       print F << "EOM";
237   <ItemGroup>
238     <ProjectReference Include="$cdup\\libgit\\libgit.vcxproj">
239       <Project>$uuid_libgit</Project>
240       <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
241     </ProjectReference>
242 EOM
243       if (!($name =~ 'xdiff')) {
244         print F << "EOM";
245     <ProjectReference Include="$cdup\\xdiff\\lib\\xdiff_lib.vcxproj">
246       <Project>$uuid_xdiff_lib</Project>
247       <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
248     </ProjectReference>
249 EOM
250       }
251       if ($name =~ /(test-(line-buffer|svn-fe)|^git-remote-testsvn)\.exe$/) {
252         my $uuid_vcs_svn_lib = $$build_structure{"LIBS_vcs-svn/lib_GUID"};
253         print F << "EOM";
254     <ProjectReference Include="$cdup\\vcs-svn\\lib\\vcs-svn_lib.vcxproj">
255       <Project>$uuid_vcs_svn_lib</Project>
256       <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
257     </ProjectReference>
258 EOM
259       }
260       print F << "EOM";
261   </ItemGroup>
262 EOM
263     }
264     print F << "EOM";
265   <Import Project="\$(VCTargetsPath)\\Microsoft.Cpp.targets" />
266 EOM
267     if (!$static_library) {
268       print F << "EOM";
269   <Target Name="${target}_AfterBuild" AfterTargets="AfterBuild">
270     <ItemGroup>
271       <DLLsAndPDBs Include="\$(VCPKGBinDirectory)\\*.dll;\$(VCPKGBinDirectory)\\*.pdb" />
272     </ItemGroup>
273     <Copy SourceFiles="@(DLLsAndPDBs)" DestinationFolder="\$(OutDir)" SkipUnchangedFiles="true" UseHardlinksIfPossible="true" />
274     <MakeDir Directories="..\\templates\\blt\\branches" />
275   </Target>
276 EOM
277     }
278     if ($target eq 'git') {
279       print F "  <Import Project=\"LinkOrCopyBuiltins.targets\" />\n";
280     }
281     if ($target eq 'git-remote-http') {
282       print F "  <Import Project=\"LinkOrCopyRemoteHttp.targets\" />\n";
283     }
284     print F << "EOM";
285 </Project>
286 EOM
287     close F;
288 }
289
290 sub createGlueProject {
291     my ($git_dir, $out_dir, $rel_dir, %build_structure) = @_;
292     print "Generate solutions file\n";
293     $rel_dir = "..\\$rel_dir";
294     $rel_dir =~ s/\//\\/g;
295     my $SLN_HEAD = "Microsoft Visual Studio Solution File, Format Version 11.00\n# Visual Studio 2010\n";
296     my $SLN_PRE  = "Project(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = ";
297     my $SLN_POST = "\nEndProject\n";
298
299     my @libs = @{$build_structure{"LIBS"}};
300     my @tmp;
301     foreach (@libs) {
302         $_ =~ s/\.a//;
303         push(@tmp, $_);
304     }
305     @libs = @tmp;
306
307     my @apps = @{$build_structure{"APPS"}};
308     @tmp = ();
309     foreach (@apps) {
310         $_ =~ s/\.exe//;
311         if ($_ eq "git" ) {
312             unshift(@tmp, $_);
313         } else {
314             push(@tmp, $_);
315         }
316     }
317     @apps = @tmp;
318
319     open F, ">git.sln" || die "Could not open git.sln for writing!\n";
320     binmode F, ":crlf :utf8";
321     print F chr(0xFEFF);
322     print F "$SLN_HEAD";
323
324     foreach (@apps) {
325         my $appname = $_;
326         my $uuid = $build_structure{"APPS_${appname}_GUID"};
327         print F "$SLN_PRE";
328         my $vcxproj = $build_structure{"APPS_${appname}_VCXPROJ"};
329         $vcxproj =~ s/\//\\/g;
330         $appname =~ s/.*\///;
331         print F "\"${appname}\", \"${vcxproj}\", \"${uuid}\"";
332         print F "$SLN_POST";
333     }
334     foreach (@libs) {
335         my $libname = $_;
336         my $uuid = $build_structure{"LIBS_${libname}_GUID"};
337         print F "$SLN_PRE";
338         my $vcxproj = $build_structure{"LIBS_${libname}_VCXPROJ"};
339         $vcxproj =~ s/\//\\/g;
340         $libname =~ s/\//_/g;
341         print F "\"${libname}\", \"${vcxproj}\", \"${uuid}\"";
342         print F "$SLN_POST";
343     }
344
345     print F << "EOM";
346 Global
347         GlobalSection(SolutionConfigurationPlatforms) = preSolution
348                 Debug|x64 = Debug|x64
349                 Debug|x86 = Debug|x86
350                 Release|x64 = Release|x64
351                 Release|x86 = Release|x86
352         EndGlobalSection
353 EOM
354     print F << "EOM";
355         GlobalSection(ProjectConfigurationPlatforms) = postSolution
356 EOM
357     foreach (@apps) {
358         my $appname = $_;
359         my $uuid = $build_structure{"APPS_${appname}_GUID"};
360         print F "\t\t${uuid}.Debug|x64.ActiveCfg = Debug|x64\n";
361         print F "\t\t${uuid}.Debug|x64.Build.0 = Debug|x64\n";
362         print F "\t\t${uuid}.Debug|x86.ActiveCfg = Debug|Win32\n";
363         print F "\t\t${uuid}.Debug|x86.Build.0 = Debug|Win32\n";
364         print F "\t\t${uuid}.Release|x64.ActiveCfg = Release|x64\n";
365         print F "\t\t${uuid}.Release|x64.Build.0 = Release|x64\n";
366         print F "\t\t${uuid}.Release|x86.ActiveCfg = Release|Win32\n";
367         print F "\t\t${uuid}.Release|x86.Build.0 = Release|Win32\n";
368     }
369     foreach (@libs) {
370         my $libname = $_;
371         my $uuid = $build_structure{"LIBS_${libname}_GUID"};
372         print F "\t\t${uuid}.Debug|x64.ActiveCfg = Debug|x64\n";
373         print F "\t\t${uuid}.Debug|x64.Build.0 = Debug|x64\n";
374         print F "\t\t${uuid}.Debug|x86.ActiveCfg = Debug|Win32\n";
375         print F "\t\t${uuid}.Debug|x86.Build.0 = Debug|Win32\n";
376         print F "\t\t${uuid}.Release|x64.ActiveCfg = Release|x64\n";
377         print F "\t\t${uuid}.Release|x64.Build.0 = Release|x64\n";
378         print F "\t\t${uuid}.Release|x86.ActiveCfg = Release|Win32\n";
379         print F "\t\t${uuid}.Release|x86.Build.0 = Release|Win32\n";
380     }
381
382     print F << "EOM";
383         EndGlobalSection
384         GlobalSection(SolutionProperties) = preSolution
385                 HideSolutionNode = FALSE
386         EndGlobalSection
387 EndGlobal
388 EOM
389     close F;
390 }
391
392 1;