Repository navigation
Expand file tree
/
Copy pathinit.sh
More file actions
executable file
·218 lines (197 loc) · 7.96 KB
/
Copy pathinit.sh
File metadata and controls
executable file
·218 lines (197 loc) · 7.96 KB
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
#!/bin/sh
#
# Creates a new Playdate app or game project from this template, renaming this placeholder to the name you provide.
#
# ./init.sh <AppName> [destination-dir] [options]
#
# AppName the game's name; it becomes the Swift module name, so letters and digits only, starting with a letter
#
# destination-dir where to create the project (default: ../<AppName>)
#
# --type <app|game> what kind of project this is; names the root class App or Game accordingly (default: game)
# --display-name <name> launcher name in pdxinfo (default: AppName)
# --bundle-id <id> reverse-DNS identifier in pdxinfo (default: com.example.<appname>)
# --author <name> author in pdxinfo (default: git config user.name)
# --no-git skip git init and the initial commit
set -eu
# Deliberately macOS-only, like the rest of the template's tooling (BSD sed below, xcrun/plutil in the build).
if [ "$(uname)" != "Darwin" ]; then
echo "error: this template is macOS-only; see README.md" >&2
exit 1
fi
usage() {
sed -n '2,18p' "$0" | sed 's/^# \{0,1\}//'
exit 64
}
# Escape a value for use as a sed replacement (&, \, and the / delimiter).
sed_escape() {
printf '%s' "$1" | sed 's/[&/\]/\\&/g'
}
name=""
dest=""
bundle_id=""
display_name=""
author=""
project_type="game"
use_git=1
while [ $# -gt 0 ]; do
case "$1" in
--display-name | --bundle-id | --author | --type)
[ $# -ge 2 ] || { echo "error: $1 requires a value" >&2; exit 64; }
case "$1" in
--display-name) display_name="$2" ;;
--bundle-id) bundle_id="$2" ;;
--author) author="$2" ;;
--type) project_type="$2" ;;
esac
shift
;;
--no-git) use_git=0 ;;
-h | --help) usage ;;
-*)
echo "error: unknown option $1" >&2
usage
;;
*)
if [ -z "$name" ]; then
name="$1"
elif [ -z "$dest" ]; then
dest="$1"
else
echo "error: unexpected argument $1" >&2
usage
fi
;;
esac
shift
done
[ -n "$name" ] || usage
case "$name" in
[A-Za-z]*) ;;
*) echo "error: name must start with a letter" >&2; exit 64 ;;
esac
if echo "$name" | LC_ALL=C grep -q '[^A-Za-z0-9]'; then
echo "error: name may only contain letters and digits (it becomes the Swift module name)" >&2
exit 64
fi
# These module names already exist in the package graph (or, for Swift, the standard library); the build would fail
# later with a conflict, so refuse them up front.
for reserved in PlaydateKit CPlaydate Swift; do
if [ "$name" = "$reserved" ]; then
echo "error: \"$name\" collides with a module the project depends on; choose another name" >&2
exit 64
fi
done
case "$project_type" in
app | game) ;;
*)
echo "error: --type must be \"app\" or \"game\" (got \"$project_type\")" >&2
exit 64
;;
esac
# The script lives at the template project's root — unless it runs on its own (e.g. piped from curl), in which case a
# temporary clone of the template provides the sources.
template_dir="$(cd "$(dirname "$0")" && pwd)"
tmp_clone=""
if [ ! -e "$template_dir/Sources/Template" ]; then
template_url="${PLAYDATE_TEMPLATE_URL:-https://github.com/rock-n-code/playdate.git}"
tmp_clone="$(mktemp -d)"
trap 'rm -rf "$tmp_clone"' EXIT
trap 'exit 130' INT TERM
echo "Fetching the template from $template_url..."
if ! git clone --quiet --depth 1 "$template_url" "$tmp_clone/template"; then
echo "error: could not clone the template from $template_url; set PLAYDATE_TEMPLATE_URL to override" >&2
exit 1
fi
template_dir="$tmp_clone/template"
# A standalone run creates the project relative to the working folder.
dest="${dest:-$PWD/$name}"
fi
lower="$(echo "$name" | tr '[:upper:]' '[:lower:]')"
dest="${dest:-$template_dir/../$name}"
bundle_id="${bundle_id:-com.example.$lower}"
display_name="${display_name:-$name}"
if [ -z "$author" ]; then
author="$(git config user.name 2>/dev/null || true)"
author="${author:-Your Name}"
fi
if [ -e "$dest" ]; then
echo "error: destination already exists: $dest" >&2
exit 1
fi
# Verify the template is intact before touching the destination, so a broken checkout fails with a clear message
# instead of a partial copy.
for entry in Package.swift Sources/Template Source/pdxinfo Makefile README.md .gitignore; do
if [ ! -e "$template_dir/$entry" ]; then
echo "error: template is missing $entry (looked in $template_dir)" >&2
exit 1
fi
done
# Never leave a half-created project behind if anything below fails or the user interrupts the run. The signal traps
# exit rather than clean up directly: sh resumes the script after a trap, so cleanup belongs in the EXIT trap alone.
trap 'rm -rf "$dest" ${tmp_clone:+"$tmp_clone"}' EXIT
trap 'exit 130' INT TERM
mkdir -p "$dest"
if git -C "$template_dir" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
# Copy the git-tracked file set (at their current working-tree state): build artifacts and editor droppings never
# reach the new project.
(cd "$template_dir" && git ls-files -z | tar --null -T - -cf -) | tar -xf - -C "$dest"
else
# The template was downloaded rather than cloned; fall back to copying the known entries and weed out binaries a
# template build left behind.
for entry in Package.swift Sources Source Makefile README.md .gitignore; do
cp -R "$template_dir/$entry" "$dest/"
done
rm -f "$dest/Source/pdex"*
fi
# This script belongs to the template, not to generated projects.
rm -f "$dest/init.sh"
# The template ships its root class as Game; app-type projects call it App.
# This runs before the name substitution below so a project name that contains "Game" is never touched by this pass.
if [ "$project_type" = "app" ]; then
mv "$dest/Sources/Template/Game.swift" "$dest/Sources/Template/App.swift"
find "$dest" -type f \( -name '*.swift' -o -name '*.md' -o -name 'Makefile' \) |
while IFS= read -r file; do
sed -i '' "s/[[:<:]]Game[[:>:]]/App/g" "$file"
done
fi
mv "$dest/Sources/Template" "$dest/Sources/$name"
# Substitute the placeholder name in the copied text files, leaving game assets alone. Only the capitalized token is
# the placeholder; the word "template" in prose stays, and pdxinfo is rewritten below.
find "$dest" -type f \( \
-name '*.swift' -o -name '*.sh' -o -name '*.md' -o \
-name 'Makefile' -o -name 'pdxinfo' -o -name '.gitignore' \
\) | while IFS= read -r file; do
# BSD sed word boundaries keep the match exact, so an identifier that merely contains "Template" would survive the
# substitution.
sed -i '' "s/[[:<:]]Template[[:>:]]/$name/g" "$file"
done
sed -i '' \
-e "s/^name=.*/name=$(sed_escape "$display_name")/" \
-e "s/^author=.*/author=$(sed_escape "$author")/" \
-e "s/^bundleID=.*/bundleID=$(sed_escape "$bundle_id")/" \
"$dest/Source/pdxinfo"
# The cloned README (and its assets folder) documents the scaffolder;
# a new project starts with its own README, titled after the app/game.
rm -rf "$dest/README.md" "$dest/.README"
printf '# %s\n' "$display_name" > "$dest/README.md"
trap - INT TERM
# The project is complete; only the temporary clone (if any) remains to be removed on exit.
trap 'rm -rf ${tmp_clone:+"$tmp_clone"}' EXIT
# Start the project as a git repository so the first change is diffable; skipped by --no-git or when the destination is
# already inside a repo.
if [ "$use_git" -eq 1 ] && ! git -C "$dest" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
if ! {
git -C "$dest" init -q &&
git -C "$dest" add -A &&
git -C "$dest" commit -qm "Initial commit."
}; then
echo "note: git initialization failed; the project itself is complete" >&2
fi
fi
echo "Created $name at $dest"
echo
echo "Next steps:"
echo " cd \"$dest\""
echo " make build # build $name.pdx for the Playdate Simulator"
echo " make # build for device + simulator (needs the Embedded Swift toolchain)"