-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmakeSwift.sh
executable file
·295 lines (256 loc) · 10.1 KB
/
makeSwift.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
#!/bin/bash
# This script is part of the MerlinMake repository
# Copyright (C) 2018,2019,2020 CoderMerlin.com
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# This script generates and executes the command line required to use the
# proper version of merlin libraries based upon a file named 'dylib.manifest'
# which must be located in the project root if dynamic libraries are required.
# If the file does not exist, no error occurs, and the commandline will be
# generated without referencing any dynamic libraries.
# The project root is the same location as .git and Package.swift (if required).
# The script will build both packages and simple directories of *.swift files.
# Arguments:
# --mode='build'|'run'|'list-dylib-paths'|'list-ld-library-path',
# default is 'build', short key is -m
# --configuration='debug'|'release', default is 'debug', short key is -c
# Dynamic library dependencies are specified in the file 'dylib.manifest' located in the same directory
# as make.sh.
# There are two line formats:
# project version -OR-
# project LOCAL path
#
# The second format facilitates simple development of libraries by easily allowing a local directory
# as a source.
# For example:
# Igis LOCAL /home/john-williams/projects/Igis
# Scenes 0.1.8
#
# An additional available option is to suffix the modifier 'MODULE' after either line format.
# This has the result of suppressing the suffix of ".build/$configuration" to the libraryPath
# This is appropriate for Modules which don't have a build process, such as CNCURSES
# For example:
# CNCURSES LOCAL /home/john-williams/projects/CNCURSES MODULE
# CNCURSES 0.1.8 MODULE
# Reference: https://stackoverflow.com/questions/192249/how-do-i-parse-command-line-arguments-in-bash
# saner programming env: these switches turn some bugs into errors
set -o errexit -o pipefail -o noclobber -o nounset
set -eu
# Reference: https://stackoverflow.com/questions/192249/how-do-i-parse-command-line-arguments-in-bash
# Read options
options=m:c:
longOptions=mode:,configuration:
! parsed=$(getopt --options=$options --longoptions=$longOptions --name "$0" -- "$@")
if [[ ${PIPESTATUS[0]} -ne 0 ]]; then
# e.g. return value is 1
# then getopt has complained about wrong arguments to stdout
exit 2
fi
# read getopt’s output this way to handle the quoting right:
eval set -- "$parsed"
# Defaults
mode='build'
configuration='debug'
while true; do
case "$1" in
-m|--mode)
mode="$2"
shift 2
;;
-c|--configuration)
configuration="$2"
shift 2
;;
--)
shift
break
;;
*)
echo "Unexpected argument $1"
exit 3
;;
esac
done
# Verify arguments
if [[ ! "$configuration" =~ ^(debug|release)$ ]]; then
echo "Unexpected configuration; must be either debug or release, not $configuration"
exit 1
fi
if [[ ! "$mode" =~ ^(build|run|list-dylib-paths|list-ld-library-path)$ ]]; then
echo "Unexpected mode; must be either build, run list-dylib-paths, or list-ld-library-path not $mode"
exit 1
fi
# Requires: MERLIN_LIBRARY_ROOT_DIR (e.g. /usr/local/lib/merlin)
[ -z "$MERLIN_LIBRARY_ROOT_DIR" ] && { echo "MERLIN_LIBRARY_ROOT_DIR must be defined"; exit 1; }
# Find the project root
# We search, in order, for Package.swift, then dylib.manifest, and finally main.swift
projectRoot=""
packagePathname=""
manifestPathname=""
mainPathname=""
packagePathname=$(upfind -name 'Package.swift' -type f 2> /dev/null | head -n 1)
if [[ -f $packagePathname ]]; then
projectRoot=$(dirname "$packagePathname")
fi
if [[ ! -d $projectRoot ]]; then
manifestPathname=$(upfind -name 'dylib.manifest' -type f 2> /dev/null | head -n 1)
if [[ -f $manifestPathname ]]; then
projectRoot=$(dirname "$manifestPathname")
fi
fi
if [[ ! -d $projectRoot ]]; then
mainPathname=$(upfind -name 'main.swift' -type f 2> /dev/null | head -n 1)
if [[ -f $mainPathname ]]; then
projectRoot=$(dirname "$mainPathname")
fi
fi
if [[ ! -d $projectRoot ]]; then
echo "Unable to determine project root from here"
exit 1
fi
# At this point, we've determined the project root
# If a manifest is present, it will be located here:
manifestPath="$projectRoot/dylib.manifest"
# Start with a basic command line and empty library path
dylibPaths=""
packageCommandLine="swift $mode -c $configuration"
# For the simple command line (no package) we add the source files
simpleBuildCommandLine="swiftc -o '$projectRoot/main'"
if [ "$configuration" == "debug" ]; then
simpleBuildCommandLine="$simpleBuildCommandLine -g"
fi
simpleRunCommandLine="'$projectRoot/main'"
# Collecting the file names in a large, package-based project can take a relatively long time
# As such, we skip this if we're building a package because, in such a case, it will simply
# be ignored anyway
if [[ ! -f $packagePathname ]]
then
while ifs= read -r line
do
simpleBuildCommandLine="$simpleBuildCommandLine '$line'"
done < <(find "$projectRoot" -name '*.swift')
fi
LD_LIBRARY_PATH=""
# Read the manifest file (if it exists)
if [ -f "$manifestPath" ]; then
# Stop here if a .dir-locals.el exists but is OLDER than the dylib.manifest
# Not updating the .dir-locals.el file will lead to odd and difficult to track down
# errors.
# However, because we often start by cloning a repository the timestamps may be very
# close to one another. Therefore, we ignore small differences, and only whine
# if the manifest is at least a minute newer than .dir-locals.el
emacsDirLocalPath="$projectRoot/.dir-locals.el"
if [ -f "$emacsDirLocalPath" ]
then
emacsDirLocalSeconds=$(date --reference="$emacsDirLocalPath" +%s)
manifestSeconds=$(date --reference="$manifestPath" +%s)
manifestNewerBySeconds=$((manifestSeconds - emacsDirLocalSeconds))
manifestNewerByOneMinute=$((manifestNewerBySeconds - 60))
if (( manifestNewerByOneMinute > 0 ))
then
echo "ERROR: '$manifestPath' has been changed but '$emacsDirLocalPath' has not."
echo " Did you mean to execute dylibEmacs?"
exit 1
fi
fi
while read line; do
if [ ! -z "$line" ]; then
# If the line is not empty, it must be in the format specified
# The first case is specification of a library found in the MERLIN_LIBRARY_ROOT_DIR
# This format specifies only a library name and version
# e.g. "Igis 1.0.7"
# or "Igis 1.0.7 MODULE"
libraryRegex='^([[:alnum:]]+)[[:space:]]+([[:alnum:]\.]+)[[:space:]]*(MODULE)?$'
# An alterntive is specifying a local path which will be used directly
# iff the tag is 'LOCAL'
# e.g. "Igis LOCAL /home/john-williams/Igis"
# or "Igis LOCAL /home/john-williams/Igis MODULE"
localRegex='^([[:alnum:]]+)[[:space:]]+LOCAL[[:space:]]+([[:alnum:]/-]+)[[:space:]]*(MODULE)?$'
if [[ "$line" =~ $localRegex ]]; then
project=${BASH_REMATCH[1]}
directory=${BASH_REMATCH[2]}
module=${BASH_REMATCH[3]}
# The library path is determined by the exact name used for the directory
libraryPath="$directory"
elif [[ "$line" =~ $libraryRegex ]]; then
project=${BASH_REMATCH[1]}
tag=${BASH_REMATCH[2]}
module=${BASH_REMATCH[3]}
# Determine the library path based on the name of the project and tag
libraryPath="$MERLIN_LIBRARY_ROOT_DIR/$project-$tag/$project"
else
echo "Unexpected line format: $line"
exit 1
fi
# If we're not dealing with a module, we need to append the acutal build directory to the path
if [[ ! "$module" =~ "MODULE" ]]; then
libraryPath="$libraryPath/.build/$configuration"
fi
# Verify that the libraryPath exists
if [[ ! -d "$libraryPath" ]]; then
echo "The specified library path was not found '$libraryPath'"
exit 1
fi
# Build the dylib paths for parsing with one path per line
if [[ ! -z "$dylibPaths" ]]; then
printf -v dylibPaths "$dylibPaths\n"
fi
dylibPaths="$dylibPaths$libraryPath"
# Build the command line by appending the library for inclusion and linking
packageCommandLine="$packageCommandLine -Xswiftc -I -Xswiftc '$libraryPath'"
packageCommandLine="$packageCommandLine -Xswiftc -L -Xswiftc '$libraryPath'"
# If this isn't a module, we need to specify use of the library
if [[ ! "$module" =~ "MODULE" ]]; then
packageCommandLine="$packageCommandLine -Xswiftc -l$project"
fi
simpleBuildCommandLine="$simpleBuildCommandLine -I '$libraryPath'"
simpleBuildCommandLine="$simpleBuildCommandLine -L '$libraryPath'"
# If this isn't a module, we need to specify use of the library
if [[ ! "$module" =~ "MODULE" ]]; then
simpleBuildCommandLine="$simpleBuildCommandLine -l$project"
fi
# Build the LD_LIBRARY_PATH
LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$libraryPath"
# Define a new variable of the form DYLIB_$project_PATH to contain the library path for the
# specified library. The library may refer to this environment variable to find files
# relative to itself.
dylibVariableName=DYLIB_${project}_PATH
printf -v $dylibVariableName "%s" "$libraryPath"
export $dylibVariableName
fi
done < "$manifestPath"
fi
# Export required variables
export LD_LIBRARY_PATH
# If the mode is run or build, we evaluate the command line
case $mode in
build|run)
if [[ -f $packagePathname ]]; then
eval "$packageCommandLine"
else
if [ "$mode" == "run" ]; then
eval "$simpleBuildCommandLine && $simpleRunCommandLine"
else
eval "$simpleBuildCommandLine"
fi
fi
;;
list-dylib-paths)
echo "$dylibPaths"
;;
list-ld-library-path)
echo "$LD_LIBRARY_PATH"
;;
*)
echo "Unexpected mode $mode"
exit 1
;;
esac