Home | History | Annotate | Download | only in make
      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 
     13 self=$0
     14 self_basename=${self##*/}
     15 self_dirname=$(dirname "$0")
     16 EOL=$'\n'
     17 
     18 show_help() {
     19     cat <<EOF
     20 Usage: ${self_basename} --name=projname [options] file1 [file2 ...]
     21 
     22 This script generates a Visual Studio project file from a list of source
     23 code files.
     24 
     25 Options:
     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
     43 EOF
     44     exit 1
     45 }
     46 
     47 die() {
     48     echo "${self_basename}: $@" >&2
     49     exit 1
     50 }
     51 
     52 die_unknown(){
     53     echo "Unknown option \"$1\"." >&2
     54     echo "See ${self_basename} --help for available options." >&2
     55     exit 1
     56 }
     57 
     58 generate_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 
     71 indent1="    "
     72 indent=""
     73 indent_push() {
     74     indent="${indent}${indent1}"
     75 }
     76 indent_pop() {
     77     indent="${indent%${indent1}}"
     78 }
     79 
     80 tag_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 
     89 open_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 
    103 close_tag() {
    104     local tag=$1
    105     indent_pop
    106     echo "${indent}</${tag}>"
    107 }
    108 
    109 tag() {
    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 
    123 tag_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 
    139 generate_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
    212 unset target
    213 for 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
    278 done
    279 outfile=${outfile:-/dev/stdout}
    280 guid=${guid:-`generate_uuid`}
    281 asm_use_custom_step=false
    282 uses_asm=${uses_asm:-false}
    283 case "${vs_ver:-11}" in
    284     10|11|12)
    285        asm_use_custom_step=$uses_asm
    286     ;;
    287 esac
    288 
    289 [ -n "$name" ] || die "Project name (--name) must be specified!"
    290 [ -n "$target" ] || die "Target (--target) must be specified!"
    291 
    292 if ${use_static_runtime:-false}; then
    293     release_runtime=MultiThreaded
    294     debug_runtime=MultiThreadedDebug
    295     lib_sfx=mt
    296 else
    297     release_runtime=MultiThreadedDLL
    298     debug_runtime=MultiThreadedDebugDLL
    299     lib_sfx=md
    300 fi
    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.
    305 for 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}"
    310 done
    311 debug_libs=${debug_libs// /;}
    312 libs=${libs// /;}
    313 
    314 
    315 # List of all platforms supported for this target
    316 case "$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     ;;
    340 esac
    341 
    342 generate_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.
    559 generate_vcxproj |
    560     sed  -e '/"/s;\([^ "]\)/;\1\\;g' |
    561     sed  -e '/xmlns/s;\\;/;g' > ${outfile}
    562 
    563 exit
    564