1#!/bin/bash
2##
3##  Copyright (c) 2010 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##*/}
15EOL=$'\n'
16EOLDOS=$'\r'
17
18show_help() {
19    cat <<EOF
20Usage: ${self_basename} [options] file1 [file2 ...]
21
22This script generates a Visual Studio solution file from a list of project
23files.
24
25Options:
26    --help                      Print this message
27    --out=outfile               Redirect output to a file
28    --ver=version               Version (7,8,9,10,11,12,14,15) of visual studio to generate for
29    --target=isa-os-cc          Target specifier
30EOF
31    exit 1
32}
33
34die() {
35    echo "${self_basename}: $@" >&2
36    [ -f "${outfile}" ] && rm -f ${outfile}{,.mk}
37    exit 1
38}
39
40die_unknown(){
41    echo "Unknown option \"$1\"." >&2
42    echo "See ${self_basename} --help for available options." >&2
43    [ -f "${outfile}" ] && rm -f ${outfile}{,.mk}
44    exit 1
45}
46
47indent1=$'\t'
48indent=""
49indent_push() {
50    indent="${indent}${indent1}"
51}
52indent_pop() {
53    indent="${indent%${indent1}}"
54}
55
56parse_project() {
57    local file=$1
58    local name=`grep RootNamespace "$file" | sed 's,.*<.*>\(.*\)</.*>.*,\1,'`
59    local guid=`grep ProjectGuid "$file" | sed 's,.*<.*>\(.*\)</.*>.*,\1,'`
60
61    # save the project GUID to a varaible, normalizing to the basename of the
62    # vcxproj file without the extension
63    local var
64    var=${file##*/}
65    var=${var%%.${sfx}}
66    eval "${var}_file=\"$1\""
67    eval "${var}_name=$name"
68    eval "${var}_guid=$guid"
69
70    cur_config_list=`grep -B1 'Label="Configuration"' $file |
71        grep Condition | cut -d\' -f4`
72    new_config_list=$(for i in $config_list $cur_config_list; do
73        echo $i
74    done | sort | uniq)
75    if [ "$config_list" != "" ] && [ "$config_list" != "$new_config_list" ]; then
76        mixed_platforms=1
77    fi
78    config_list="$new_config_list"
79    eval "${var}_config_list=\"$cur_config_list\""
80    proj_list="${proj_list} ${var}"
81}
82
83process_project() {
84    eval "local file=\${$1_file}"
85    eval "local name=\${$1_name}"
86    eval "local guid=\${$1_guid}"
87
88    # save the project GUID to a varaible, normalizing to the basename of the
89    # vcproj file without the extension
90    local var
91    var=${file##*/}
92    var=${var%%.${sfx}}
93    eval "${var}_guid=$guid"
94
95    echo "Project(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"$name\", \"$file\", \"$guid\""
96    echo "EndProject"
97}
98
99process_global() {
100    echo "Global"
101    indent_push
102
103    #
104    # Solution Configuration Platforms
105    #
106    echo "${indent}GlobalSection(SolutionConfigurationPlatforms) = preSolution"
107    indent_push
108    IFS_bak=${IFS}
109    IFS=$'\r'$'\n'
110    if [ "$mixed_platforms" != "" ]; then
111        config_list="
112Release|Mixed Platforms
113Debug|Mixed Platforms"
114    fi
115    for config in ${config_list}; do
116        echo "${indent}$config = $config"
117    done
118    IFS=${IFS_bak}
119    indent_pop
120    echo "${indent}EndGlobalSection"
121
122    #
123    # Project Configuration Platforms
124    #
125    echo "${indent}GlobalSection(ProjectConfigurationPlatforms) = postSolution"
126    indent_push
127    for proj in ${proj_list}; do
128        eval "local proj_guid=\${${proj}_guid}"
129        eval "local proj_config_list=\${${proj}_config_list}"
130        IFS=$'\r'$'\n'
131        for config in ${proj_config_list}; do
132            if [ "$mixed_platforms" != "" ]; then
133                local c=${config%%|*}
134                echo "${indent}${proj_guid}.${c}|Mixed Platforms.ActiveCfg = ${config}"
135                echo "${indent}${proj_guid}.${c}|Mixed Platforms.Build.0 = ${config}"
136            else
137                echo "${indent}${proj_guid}.${config}.ActiveCfg = ${config}"
138                echo "${indent}${proj_guid}.${config}.Build.0 = ${config}"
139            fi
140
141        done
142        IFS=${IFS_bak}
143    done
144    indent_pop
145    echo "${indent}EndGlobalSection"
146
147    #
148    # Solution Properties
149    #
150    echo "${indent}GlobalSection(SolutionProperties) = preSolution"
151    indent_push
152    echo "${indent}HideSolutionNode = FALSE"
153    indent_pop
154    echo "${indent}EndGlobalSection"
155
156    indent_pop
157    echo "EndGlobal"
158}
159
160process_makefile() {
161    IFS_bak=${IFS}
162    IFS=$'\r'$'\n'
163    local TAB=$'\t'
164    cat <<EOF
165MSBUILD_TOOL := msbuild.exe
166found_devenv := \$(shell which \$(MSBUILD_TOOL) >/dev/null 2>&1 && echo yes)
167.nodevenv.once:
168${TAB}@echo "  * \$(MSBUILD_TOOL) not found in path."
169${TAB}@echo "  * "
170${TAB}@echo "  * You will have to build all configurations manually using the"
171${TAB}@echo "  * Visual Studio IDE. To allow make to build them automatically,"
172${TAB}@echo "  * add the Common7/IDE directory of your Visual Studio"
173${TAB}@echo "  * installation to your path, eg:"
174${TAB}@echo "  *   C:\Program Files\Microsoft Visual Studio 10.0\Common7\IDE"
175${TAB}@echo "  * "
176${TAB}@touch \$@
177CLEAN-OBJS += \$(if \$(found_devenv),,.nodevenv.once)
178
179EOF
180
181    for sln_config in ${config_list}; do
182        local config=${sln_config%%|*}
183        local platform=${sln_config##*|}
184        local nows_sln_config=`echo $sln_config | sed -e 's/[^a-zA-Z0-9]/_/g'`
185        cat <<EOF
186BUILD_TARGETS += \$(if \$(NO_LAUNCH_DEVENV),,$nows_sln_config)
187clean::
188${TAB}rm -rf "$platform"/"$config"
189.PHONY: $nows_sln_config
190ifneq (\$(found_devenv),)
191$nows_sln_config: $outfile
192${TAB}\$(MSBUILD_TOOL) $outfile -m -t:Build \\
193${TAB}${TAB}-p:Configuration="$config" -p:Platform="$platform"
194else
195$nows_sln_config: $outfile .nodevenv.once
196${TAB}@echo "  * Skipping build of $sln_config (\$(MSBUILD_TOOL) not in path)."
197${TAB}@echo "  * "
198endif
199
200EOF
201    done
202    IFS=${IFS_bak}
203}
204
205# Process command line
206outfile=/dev/stdout
207for opt in "$@"; do
208    optval="${opt#*=}"
209    case "$opt" in
210    --help|-h) show_help
211    ;;
212    --out=*) outfile="${optval}"; mkoutfile="${optval}".mk
213    ;;
214    --dep=*) eval "${optval%%:*}_deps=\"\${${optval%%:*}_deps} ${optval##*:}\""
215    ;;
216    --ver=*) vs_ver="$optval"
217             case $optval in
218             10|11|12|14|15)
219             ;;
220             *) die Unrecognized Visual Studio Version in $opt
221             ;;
222             esac
223    ;;
224    --target=*) target="${optval}"
225    ;;
226    -*) die_unknown $opt
227    ;;
228    *) file_list[${#file_list[@]}]="$opt"
229    esac
230done
231outfile=${outfile:-/dev/stdout}
232mkoutfile=${mkoutfile:-/dev/stdout}
233case "${vs_ver:-10}" in
234    10) sln_vers="11.00"
235       sln_vers_str="Visual Studio 2010"
236    ;;
237    11) sln_vers="12.00"
238       sln_vers_str="Visual Studio 2012"
239    ;;
240    12) sln_vers="12.00"
241       sln_vers_str="Visual Studio 2013"
242    ;;
243    14) sln_vers="12.00"
244       sln_vers_str="Visual Studio 2015"
245    ;;
246    15) sln_vers="12.00"
247       sln_vers_str="Visual Studio 2017"
248    ;;
249esac
250sfx=vcxproj
251
252for f in "${file_list[@]}"; do
253    parse_project $f
254done
255cat  >${outfile} <<EOF
256Microsoft Visual Studio Solution File, Format Version $sln_vers${EOLDOS}
257# $sln_vers_str${EOLDOS}
258EOF
259for proj in ${proj_list}; do
260    process_project $proj >>${outfile}
261done
262process_global >>${outfile}
263process_makefile >${mkoutfile}
264