1#!/bin/bash
2##
3##  Copyright (c) 2013 The WebM project authors. All Rights Reserved.
4##
5##  Use of this source code is governed by a BSD-style license
6##  that can be found in the LICENSE file in the root of the source
7##  tree. An additional intellectual property rights grant can be found
8##  in the file PATENTS.  All contributing project authors may
9##  be found in the AUTHORS file in the root of the source tree.
10##
11
12
13self=$0
14self_basename=${self##*/}
15self_dirname=$(dirname "$0")
16EOL=$'\n'
17
18show_help() {
19    cat <<EOF
20Usage: ${self_basename} --name=projname [options] file1 [file2 ...]
21
22This script generates a Visual Studio project file from a list of source
23code files.
24
25Options:
26    --help                      Print this message
27    --exe                       Generate a project for building an Application
28    --lib                       Generate a project for creating a static library
29    --dll                       Generate a project for creating a dll
30    --static-crt                Use the static C runtime (/MT)
31    --enable-werror             Treat warnings as errors (/WX)
32    --target=isa-os-cc          Target specifier (required)
33    --out=filename              Write output to a file [stdout]
34    --name=project_name         Name of the project (required)
35    --proj-guid=GUID            GUID to use for the project
36    --module-def=filename       File containing export definitions (for DLLs)
37    --ver=version               Version (10,11,12) of visual studio to generate for
38    --src-path-bare=dir         Path to root of source tree
39    -Ipath/to/include           Additional include directories
40    -DFLAG[=value]              Preprocessor macros to define
41    -Lpath/to/lib               Additional library search paths
42    -llibname                   Library to link against
43EOF
44    exit 1
45}
46
47die() {
48    echo "${self_basename}: $@" >&2
49    exit 1
50}
51
52die_unknown(){
53    echo "Unknown option \"$1\"." >&2
54    echo "See ${self_basename} --help for available options." >&2
55    exit 1
56}
57
58generate_uuid() {
59    local hex="0123456789ABCDEF"
60    local i
61    local uuid=""
62    local j
63    #93995380-89BD-4b04-88EB-625FBE52EBFB
64    for ((i=0; i<32; i++)); do
65        (( j = $RANDOM % 16 ))
66        uuid="${uuid}${hex:$j:1}"
67    done
68    echo "${uuid:0:8}-${uuid:8:4}-${uuid:12:4}-${uuid:16:4}-${uuid:20:12}"
69}
70
71indent1="    "
72indent=""
73indent_push() {
74    indent="${indent}${indent1}"
75}
76indent_pop() {
77    indent="${indent%${indent1}}"
78}
79
80tag_attributes() {
81    for opt in "$@"; do
82        optval="${opt#*=}"
83        [ -n "${optval}" ] ||
84            die "Missing attribute value in '$opt' while generating $tag tag"
85        echo "${indent}${opt%%=*}=\"${optval}\""
86    done
87}
88
89open_tag() {
90    local tag=$1
91    shift
92    if [ $# -ne 0 ]; then
93        echo "${indent}<${tag}"
94        indent_push
95        tag_attributes "$@"
96        echo "${indent}>"
97    else
98        echo "${indent}<${tag}>"
99        indent_push
100    fi
101}
102
103close_tag() {
104    local tag=$1
105    indent_pop
106    echo "${indent}</${tag}>"
107}
108
109tag() {
110    local tag=$1
111    shift
112    if [ $# -ne 0 ]; then
113        echo "${indent}<${tag}"
114        indent_push
115        tag_attributes "$@"
116        indent_pop
117        echo "${indent}/>"
118    else
119        echo "${indent}<${tag}/>"
120    fi
121}
122
123tag_content() {
124    local tag=$1
125    local content=$2
126    shift
127    shift
128    if [ $# -ne 0 ]; then
129        echo "${indent}<${tag}"
130        indent_push
131        tag_attributes "$@"
132        echo "${indent}>${content}</${tag}>"
133        indent_pop
134    else
135        echo "${indent}<${tag}>${content}</${tag}>"
136    fi
137}
138
139generate_filter() {
140    local name=$1
141    local pats=$2
142    local file_list_sz
143    local i
144    local f
145    local saveIFS="$IFS"
146    local pack
147    echo "generating filter '$name' from ${#file_list[@]} files" >&2
148    IFS=*
149
150    file_list_sz=${#file_list[@]}
151    for i in ${!file_list[@]}; do
152        f=${file_list[i]}
153        for pat in ${pats//;/$IFS}; do
154            if [ "${f##*.}" == "$pat" ]; then
155                unset file_list[i]
156
157                objf=$(echo ${f%.*}.obj | sed -e 's/^[\./]\+//g' -e 's,/,_,g')
158
159                if ([ "$pat" == "asm" ] || [ "$pat" == "s" ]) && $asm_use_custom_step; then
160                    # Avoid object file name collisions, i.e. vpx_config.c and
161                    # vpx_config.asm produce the same object file without
162                    # this additional suffix.
163                    objf=${objf%.obj}_asm.obj
164                    open_tag CustomBuild \
165                        Include=".\\$f"
166                    for plat in "${platforms[@]}"; do
167                        for cfg in Debug Release; do
168                            tag_content Message "Assembling %(Filename)%(Extension)" \
169                                Condition="'\$(Configuration)|\$(Platform)'=='$cfg|$plat'"
170                            tag_content Command "$(eval echo \$asm_${cfg}_cmdline) -o \$(IntDir)$objf" \
171                                Condition="'\$(Configuration)|\$(Platform)'=='$cfg|$plat'"
172                            tag_content Outputs "\$(IntDir)$objf" \
173                                Condition="'\$(Configuration)|\$(Platform)'=='$cfg|$plat'"
174                        done
175                    done
176                    close_tag CustomBuild
177                elif [ "$pat" == "c" ] || \
178                     [ "$pat" == "cc" ] || [ "$pat" == "cpp" ]; then
179                    open_tag ClCompile \
180                        Include=".\\$f"
181                    # Separate file names with Condition?
182                    tag_content ObjectFileName "\$(IntDir)$objf"
183                    # Check for AVX and turn it on to avoid warnings.
184                    if [[ $f =~ avx.?\.c$ ]]; then
185                        tag_content AdditionalOptions "/arch:AVX"
186                    fi
187                    close_tag ClCompile
188                elif [ "$pat" == "h" ] ; then
189                    tag ClInclude \
190                        Include=".\\$f"
191                elif [ "$pat" == "vcxproj" ] ; then
192                    open_tag ProjectReference \
193                        Include="$f"
194                    depguid=`grep ProjectGuid "$f" | sed 's,.*<.*>\(.*\)</.*>.*,\1,'`
195                    tag_content Project "$depguid"
196                    tag_content ReferenceOutputAssembly false
197                    close_tag ProjectReference
198                else
199                    tag None \
200                        Include=".\\$f"
201                fi
202
203                break
204            fi
205        done
206    done
207
208    IFS="$saveIFS"
209}
210
211# Process command line
212unset target
213for opt in "$@"; do
214    optval="${opt#*=}"
215    case "$opt" in
216        --help|-h) show_help
217        ;;
218        --target=*) target="${optval}"
219        ;;
220        --out=*) outfile="$optval"
221        ;;
222        --name=*) name="${optval}"
223        ;;
224        --proj-guid=*) guid="${optval}"
225        ;;
226        --module-def=*) module_def="${optval}"
227        ;;
228        --exe) proj_kind="exe"
229        ;;
230        --dll) proj_kind="dll"
231        ;;
232        --lib) proj_kind="lib"
233        ;;
234        --src-path-bare=*) src_path_bare="$optval"
235        ;;
236        --static-crt) use_static_runtime=true
237        ;;
238        --enable-werror) werror=true
239        ;;
240        --ver=*)
241            vs_ver="$optval"
242            case "$optval" in
243                10|11|12)
244                ;;
245                *) die Unrecognized Visual Studio Version in $opt
246                ;;
247            esac
248        ;;
249        -I*)
250            opt="${opt%/}"
251            incs="${incs}${incs:+;}${opt##-I}"
252            yasmincs="${yasmincs} ${opt}"
253        ;;
254        -D*) defines="${defines}${defines:+;}${opt##-D}"
255        ;;
256        -L*) # fudge . to $(OutDir)
257            if [ "${opt##-L}" == "." ]; then
258                libdirs="${libdirs}${libdirs:+;}\$(OutDir)"
259            else
260                 # Also try directories for this platform/configuration
261                 libdirs="${libdirs}${libdirs:+;}${opt##-L}"
262                 libdirs="${libdirs}${libdirs:+;}${opt##-L}/\$(PlatformName)/\$(Configuration)"
263                 libdirs="${libdirs}${libdirs:+;}${opt##-L}/\$(PlatformName)"
264            fi
265        ;;
266        -l*) libs="${libs}${libs:+ }${opt##-l}.lib"
267        ;;
268        -*) die_unknown $opt
269        ;;
270        *)
271            file_list[${#file_list[@]}]="$opt"
272            case "$opt" in
273                 *.asm|*.s) uses_asm=true
274                 ;;
275            esac
276        ;;
277    esac
278done
279outfile=${outfile:-/dev/stdout}
280guid=${guid:-`generate_uuid`}
281asm_use_custom_step=false
282uses_asm=${uses_asm:-false}
283case "${vs_ver:-11}" in
284    10|11|12)
285       asm_use_custom_step=$uses_asm
286    ;;
287esac
288
289[ -n "$name" ] || die "Project name (--name) must be specified!"
290[ -n "$target" ] || die "Target (--target) must be specified!"
291
292if ${use_static_runtime:-false}; then
293    release_runtime=MultiThreaded
294    debug_runtime=MultiThreadedDebug
295    lib_sfx=mt
296else
297    release_runtime=MultiThreadedDLL
298    debug_runtime=MultiThreadedDebugDLL
299    lib_sfx=md
300fi
301
302# Calculate debug lib names: If a lib ends in ${lib_sfx}.lib, then rename
303# it to ${lib_sfx}d.lib. This precludes linking to release libs from a
304# debug exe, so this may need to be refactored later.
305for lib in ${libs}; do
306    if [ "$lib" != "${lib%${lib_sfx}.lib}" ]; then
307        lib=${lib%.lib}d.lib
308    fi
309    debug_libs="${debug_libs}${debug_libs:+ }${lib}"
310done
311debug_libs=${debug_libs// /;}
312libs=${libs// /;}
313
314
315# List of all platforms supported for this target
316case "$target" in
317    x86_64*)
318        platforms[0]="x64"
319        asm_Debug_cmdline="yasm -Xvc -g cv8 -f \$(PlatformName) ${yasmincs} &quot;%(FullPath)&quot;"
320        asm_Release_cmdline="yasm -Xvc -f \$(PlatformName) ${yasmincs} &quot;%(FullPath)&quot;"
321    ;;
322    x86*)
323        platforms[0]="Win32"
324        asm_Debug_cmdline="yasm -Xvc -g cv8 -f \$(PlatformName) ${yasmincs} &quot;%(FullPath)&quot;"
325        asm_Release_cmdline="yasm -Xvc -f \$(PlatformName) ${yasmincs} &quot;%(FullPath)&quot;"
326    ;;
327    arm*)
328        asm_Debug_cmdline="armasm -nologo &quot;%(FullPath)&quot;"
329        asm_Release_cmdline="armasm -nologo &quot;%(FullPath)&quot;"
330        if [ "$name" = "obj_int_extract" ]; then
331            # We don't want to build this tool for the target architecture,
332            # but for an architecture we can run locally during the build.
333            platforms[0]="Win32"
334        else
335            platforms[0]="ARM"
336        fi
337    ;;
338    *) die "Unsupported target $target!"
339    ;;
340esac
341
342generate_vcxproj() {
343    echo "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
344    open_tag Project \
345        DefaultTargets="Build" \
346        ToolsVersion="4.0" \
347        xmlns="http://schemas.microsoft.com/developer/msbuild/2003" \
348
349    open_tag ItemGroup \
350        Label="ProjectConfigurations"
351    for plat in "${platforms[@]}"; do
352        for config in Debug Release; do
353            open_tag ProjectConfiguration \
354                Include="$config|$plat"
355            tag_content Configuration $config
356            tag_content Platform $plat
357            close_tag ProjectConfiguration
358        done
359    done
360    close_tag ItemGroup
361
362    open_tag PropertyGroup \
363        Label="Globals"
364        tag_content ProjectGuid "{${guid}}"
365        tag_content RootNamespace ${name}
366        tag_content Keyword ManagedCProj
367    close_tag PropertyGroup
368
369    tag Import \
370        Project="\$(VCTargetsPath)\\Microsoft.Cpp.Default.props"
371
372    for plat in "${platforms[@]}"; do
373        for config in Release Debug; do
374            open_tag PropertyGroup \
375                Condition="'\$(Configuration)|\$(Platform)'=='$config|$plat'" \
376                Label="Configuration"
377            if [ "$proj_kind" = "exe" ]; then
378                tag_content ConfigurationType Application
379            elif [ "$proj_kind" = "dll" ]; then
380                tag_content ConfigurationType DynamicLibrary
381            else
382                tag_content ConfigurationType StaticLibrary
383            fi
384            if [ "$vs_ver" = "11" ]; then
385                if [ "$plat" = "ARM" ]; then
386                    # Setting the wp80 toolchain automatically sets the
387                    # WINAPI_FAMILY define, which is required for building
388                    # code for arm with the windows headers. Alternatively,
389                    # one could add AppContainerApplication=true in the Globals
390                    # section and add PrecompiledHeader=NotUsing and
391                    # CompileAsWinRT=false in ClCompile and SubSystem=Console
392                    # in Link.
393                    tag_content PlatformToolset v110_wp80
394                else
395                    tag_content PlatformToolset v110
396                fi
397            fi
398            if [ "$vs_ver" = "12" ]; then
399                if [ "$plat" = "ARM" ]; then
400                    # Setting the wp80 toolchain automatically sets the
401                    # WINAPI_FAMILY define, which is required for building
402                    # code for arm with the windows headers. Alternatively,
403                    # one could add AppContainerApplication=true in the Globals
404                    # section and add PrecompiledHeader=NotUsing and
405                    # CompileAsWinRT=false in ClCompile and SubSystem=Console
406                    # in Link.
407                    tag_content PlatformToolset v120_wp80
408                else
409                    tag_content PlatformToolset v120
410                fi
411            fi
412            tag_content CharacterSet Unicode
413            if [ "$config" = "Release" ]; then
414                tag_content WholeProgramOptimization true
415            fi
416            close_tag PropertyGroup
417        done
418    done
419
420    tag Import \
421        Project="\$(VCTargetsPath)\\Microsoft.Cpp.props"
422
423    open_tag ImportGroup \
424        Label="PropertySheets"
425        tag Import \
426            Project="\$(UserRootDir)\\Microsoft.Cpp.\$(Platform).user.props" \
427            Condition="exists('\$(UserRootDir)\\Microsoft.Cpp.\$(Platform).user.props')" \
428            Label="LocalAppDataPlatform"
429    close_tag ImportGroup
430
431    tag PropertyGroup \
432        Label="UserMacros"
433
434    for plat in "${platforms[@]}"; do
435        plat_no_ws=`echo $plat | sed 's/[^A-Za-z0-9_]/_/g'`
436        for config in Debug Release; do
437            open_tag PropertyGroup \
438                Condition="'\$(Configuration)|\$(Platform)'=='$config|$plat'"
439            tag_content OutDir "\$(SolutionDir)$plat_no_ws\\\$(Configuration)\\"
440            tag_content IntDir "$plat_no_ws\\\$(Configuration)\\${name}\\"
441            if [ "$proj_kind" == "lib" ]; then
442              if [ "$config" == "Debug" ]; then
443                config_suffix=d
444              else
445                config_suffix=""
446              fi
447              tag_content TargetName "${name}${lib_sfx}${config_suffix}"
448            fi
449            close_tag PropertyGroup
450        done
451    done
452
453    for plat in "${platforms[@]}"; do
454        for config in Debug Release; do
455            open_tag ItemDefinitionGroup \
456                Condition="'\$(Configuration)|\$(Platform)'=='$config|$plat'"
457            if [ "$name" == "vpx" ]; then
458                hostplat=$plat
459                if [ "$hostplat" == "ARM" ]; then
460                    hostplat=Win32
461                fi
462                open_tag PreBuildEvent
463                tag_content Command "call obj_int_extract.bat $src_path_bare $hostplat\\\$(Configuration)"
464                close_tag PreBuildEvent
465            fi
466            open_tag ClCompile
467            if [ "$config" = "Debug" ]; then
468                opt=Disabled
469                runtime=$debug_runtime
470                curlibs=$debug_libs
471                case "$name" in
472                obj_int_extract)
473                    debug=DEBUG
474                    ;;
475                *)
476                    debug=_DEBUG
477                    ;;
478                esac
479            else
480                opt=MaxSpeed
481                runtime=$release_runtime
482                curlibs=$libs
483                tag_content FavorSizeOrSpeed Speed
484                debug=NDEBUG
485            fi
486            case "$name" in
487            obj_int_extract)
488                extradefines=";_CONSOLE"
489                ;;
490            *)
491                extradefines=";$defines"
492                ;;
493            esac
494            tag_content Optimization $opt
495            tag_content AdditionalIncludeDirectories "$incs;%(AdditionalIncludeDirectories)"
496            tag_content PreprocessorDefinitions "WIN32;$debug;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_DEPRECATE$extradefines;%(PreprocessorDefinitions)"
497            tag_content RuntimeLibrary $runtime
498            tag_content WarningLevel Level3
499            if ${werror:-false}; then
500                tag_content TreatWarningAsError true
501            fi
502            close_tag ClCompile
503            case "$proj_kind" in
504            exe)
505                open_tag Link
506                if [ "$name" != "obj_int_extract" ]; then
507                    tag_content AdditionalDependencies "$curlibs"
508                    tag_content AdditionalLibraryDirectories "$libdirs;%(AdditionalLibraryDirectories)"
509                fi
510                tag_content GenerateDebugInformation true
511                close_tag Link
512                ;;
513            dll)
514                open_tag Link
515                tag_content GenerateDebugInformation true
516                tag_content ModuleDefinitionFile $module_def
517                close_tag Link
518                ;;
519            lib)
520                ;;
521            esac
522            close_tag ItemDefinitionGroup
523        done
524
525    done
526
527    open_tag ItemGroup
528    generate_filter "Source Files"   "c;cc;cpp;def;odl;idl;hpj;bat;asm;asmx;s"
529    close_tag ItemGroup
530    open_tag ItemGroup
531    generate_filter "Header Files"   "h;hm;inl;inc;xsd"
532    close_tag ItemGroup
533    open_tag ItemGroup
534    generate_filter "Build Files"    "mk"
535    close_tag ItemGroup
536    open_tag ItemGroup
537    generate_filter "References"     "vcxproj"
538    close_tag ItemGroup
539
540    tag Import \
541        Project="\$(VCTargetsPath)\\Microsoft.Cpp.targets"
542
543    open_tag ImportGroup \
544        Label="ExtensionTargets"
545    close_tag ImportGroup
546
547    close_tag Project
548
549    # This must be done from within the {} subshell
550    echo "Ignored files list (${#file_list[@]} items) is:" >&2
551    for f in "${file_list[@]}"; do
552        echo "    $f" >&2
553    done
554}
555
556# This regexp doesn't catch most of the strings in the vcxproj format,
557# since they're like <tag>path</tag> instead of <tag attr="path" />
558# as previously. It still seems to work ok despite this.
559generate_vcxproj |
560    sed  -e '/"/s;\([^ "]\)/;\1\\;g' |
561    sed  -e '/xmlns/s;\\;/;g' > ${outfile}
562
563exit
564