Skip to content

Commit 1dc6582

Browse files
committed
adding build.sh back
1 parent 2582b63 commit 1dc6582

1 file changed

Lines changed: 319 additions & 0 deletions

File tree

build.sh

Lines changed: 319 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,319 @@
1+
#!/usr/bin/env bash
2+
3+
# Batch runner for TopoGen: run generate+build for each config in a folder.
4+
#
5+
# Usage:
6+
# ./build.sh [--include PATTERN ...] [--exclude PATTERN ...] [--force] [--build-only] <configs_dir> <output_dir>
7+
#
8+
# Example:
9+
# ./build.sh examples scenarios
10+
# ./build.sh --include "small_*" --exclude "*clos*" examples scenarios
11+
# ./build.sh --force examples scenarios
12+
# ./build.sh --build-only examples scenarios
13+
#
14+
# Behavior:
15+
# - Finds .yml/.yaml files directly under <configs_dir> (no recursion).
16+
# - For each config, creates <output_dir>/<config_stem>/ and runs both stages
17+
# in that directory so artefacts are kept together:
18+
# <config_stem>_integrated_graph.json
19+
# <config_stem>_scenario.yml
20+
# generate.log, build.log
21+
# - Prints a concise emoji summary at the end.
22+
23+
set -u -o pipefail
24+
25+
die() {
26+
echo "$*" >&2
27+
exit 1
28+
}
29+
30+
# Resolve an absolute path without relying on realpath.
31+
abs_path() {
32+
local p="$1"
33+
local dir base
34+
dir=$(cd "$(dirname -- "$p")" && pwd) || return 1
35+
base=$(basename -- "$p")
36+
printf '%s/%s' "$dir" "$base"
37+
}
38+
39+
# Parse options: --include/--exclude support multiple occurrences
40+
INCLUDE_PATTERNS=()
41+
EXCLUDE_PATTERNS=()
42+
FORCE=false
43+
BUILD_ONLY=false
44+
45+
print_usage() {
46+
cat >&2 <<EOF
47+
Usage: $0 [--include PATTERN ...] [--exclude PATTERN ...] [--force] [--build-only] <configs_dir> <output_dir>
48+
49+
Examples:
50+
$0 examples scenarios
51+
$0 --include "small_*" examples scenarios
52+
$0 --exclude "*clos*" examples scenarios
53+
$0 --include "small_*" --exclude "*clos*" examples scenarios
54+
$0 --force examples scenarios
55+
$0 --build-only examples scenarios
56+
57+
Notes:
58+
- PATTERNs are shell globs matched against the config file basename (e.g., small_test.yml).
59+
- Multiple --include patterns act as OR; --exclude patterns remove matches.
60+
- --force ignores cached integrated graphs and runs generation unconditionally.
61+
- --build-only skips the generate stage and runs only the build stage.
62+
EOF
63+
}
64+
65+
ARGS=()
66+
while [[ $# -gt 0 ]]; do
67+
case "$1" in
68+
--include)
69+
shift || true
70+
[[ ${1-} ]] || die "Missing PATTERN after --include"
71+
INCLUDE_PATTERNS+=("$1"); shift || true ;;
72+
--exclude)
73+
shift || true
74+
[[ ${1-} ]] || die "Missing PATTERN after --exclude"
75+
EXCLUDE_PATTERNS+=("$1"); shift || true ;;
76+
--force)
77+
FORCE=true; shift || true ;;
78+
--build-only)
79+
BUILD_ONLY=true; shift || true ;;
80+
-h|--help)
81+
print_usage; exit 0 ;;
82+
--)
83+
shift; break ;;
84+
--*)
85+
die "Unknown option: $1" ;;
86+
*)
87+
ARGS+=("$1"); shift || true ;;
88+
esac
89+
done
90+
91+
# Append any remaining args
92+
if [[ $# -gt 0 ]]; then
93+
ARGS+=("$@")
94+
fi
95+
96+
if [[ ${#ARGS[@]} -ne 2 ]]; then
97+
print_usage
98+
exit 2
99+
fi
100+
101+
CONFIGS_DIR_RAW="${ARGS[0]}"
102+
OUTPUT_DIR_RAW="${ARGS[1]}"
103+
104+
[[ -d "$CONFIGS_DIR_RAW" ]] || die "Configs directory not found: $CONFIGS_DIR_RAW"
105+
mkdir -p "$OUTPUT_DIR_RAW" || die "Cannot create output directory: $OUTPUT_DIR_RAW"
106+
107+
# Canonical absolute paths
108+
CONFIGS_DIR=$(abs_path "$CONFIGS_DIR_RAW")
109+
OUTPUT_DIR=$(abs_path "$OUTPUT_DIR_RAW")
110+
111+
# Detect TopoGen invoker once: prefer installed CLI; otherwise python -m.
112+
TOPGEN_INVOKE=(topogen)
113+
if ! command -v "${TOPGEN_INVOKE[0]}" >/dev/null 2>&1; then
114+
if command -v python3 >/dev/null 2>&1; then
115+
TOPGEN_INVOKE=(python3 -m topogen)
116+
elif command -v python >/dev/null 2>&1; then
117+
TOPGEN_INVOKE=(python -m topogen)
118+
else
119+
die "Neither 'topogen' nor Python found on PATH. Activate your venv or install TopoGen."
120+
fi
121+
fi
122+
123+
echo "🚀 TopoGen batch run"
124+
OUTPUT_DIR_PRINT=${OUTPUT_DIR%/.}
125+
CONFIGS_DIR_PRINT=${CONFIGS_DIR%/.}
126+
echo "📁 Configs: $CONFIGS_DIR_PRINT"
127+
echo "📂 Output: $OUTPUT_DIR_PRINT"
128+
if [[ ${#INCLUDE_PATTERNS[@]} -gt 0 ]]; then
129+
echo "🔎 Include: ${INCLUDE_PATTERNS[*]}"
130+
else
131+
echo "🔎 Include: (none)"
132+
fi
133+
if [[ ${#EXCLUDE_PATTERNS[@]} -gt 0 ]]; then
134+
echo "🔎 Exclude: ${EXCLUDE_PATTERNS[*]}"
135+
else
136+
echo "🔎 Exclude: (none)"
137+
fi
138+
echo "⚙️ Force: $FORCE"
139+
echo "🏗️ Build-only: $BUILD_ONLY"
140+
echo
141+
142+
# Collect summary statistics
143+
total=0
144+
gen_ok=0
145+
gen_fail=0
146+
gen_config=0
147+
gen_cached=0
148+
build_ok=0
149+
build_validation=0
150+
build_runtime=0
151+
build_config=0
152+
build_skipped=0
153+
build_other=0
154+
155+
row_names=()
156+
row_gen=()
157+
row_build=()
158+
w_name=6 # min width for header 'Config'
159+
w_gen=8 # min width for header 'Generate'
160+
w_build=5 # min width for header 'Build'
161+
162+
passes_filters() {
163+
# return 0 if basename passes include/exclude filters, else 1
164+
local name="$1"
165+
# Includes: if provided, require any to match
166+
if [[ ${#INCLUDE_PATTERNS[@]} -gt 0 ]]; then
167+
local any=1
168+
for pat in "${INCLUDE_PATTERNS[@]}"; do
169+
if [[ $name == $pat ]]; then any=0; break; fi
170+
done
171+
if (( any != 0 )); then return 1; fi
172+
fi
173+
# Excludes: drop if any matches
174+
if [[ ${#EXCLUDE_PATTERNS[@]} -gt 0 ]]; then
175+
for pat in "${EXCLUDE_PATTERNS[@]}"; do
176+
if [[ $name == $pat ]]; then return 1; fi
177+
done
178+
fi
179+
return 0
180+
}
181+
182+
# Enumerate configs (non-recursive)
183+
while IFS= read -r -d '' cfg; do
184+
cfg_abs=$(abs_path "$cfg")
185+
cfg_name=$(basename -- "$cfg")
186+
# Apply include/exclude filters on basename
187+
if ! passes_filters "$cfg_name"; then
188+
continue
189+
fi
190+
total=$((total + 1))
191+
stem=${cfg_name%.*}
192+
workdir="$OUTPUT_DIR/$stem"
193+
mkdir -p "$workdir" || die "Cannot create work directory: $workdir"
194+
195+
echo "➡️ Processing $cfg_name"
196+
echo " 📦 Workdir: $(abs_path "$workdir")"
197+
198+
graph_work="$workdir/${stem}_integrated_graph.json"
199+
200+
gen_ec=0
201+
if [[ "$BUILD_ONLY" == "true" ]]; then
202+
# Explicitly skip generate stage, proceed straight to build
203+
echo "⏭️ Skipping generate due to --build-only" | tee "$workdir/generate.log" >/dev/null
204+
gen_ec=100 # treat as cached/ready so build runs
205+
else
206+
if [[ "$FORCE" == "true" ]]; then
207+
run_generate=true
208+
else
209+
if [[ -f "$graph_work" ]]; then
210+
run_generate=false
211+
else
212+
run_generate=true
213+
fi
214+
fi
215+
216+
if [[ "$run_generate" == "true" ]]; then
217+
# Run generate writing artefacts directly to workdir via -o
218+
("${TOPGEN_INVOKE[@]}" generate "$cfg_abs" -o "$workdir") 2>&1 | tee "$workdir/generate.log"
219+
gen_ec=${PIPESTATUS[0]}
220+
else
221+
# Use cached artefacts
222+
: # artefacts already in workdir from previous run
223+
echo "⏭️ Skipping generate: found existing ${stem}_integrated_graph.json" | tee "$workdir/generate.log" >/dev/null
224+
gen_ec=100 # special code for 'cached'
225+
fi
226+
fi
227+
228+
if [[ $gen_ec -eq 0 ]]; then
229+
gen_icon=""
230+
gen_ok=$((gen_ok + 1))
231+
elif [[ $gen_ec -eq 2 ]]; then
232+
gen_icon=""
233+
gen_config=$((gen_config + 1))
234+
elif [[ $gen_ec -eq 100 ]]; then
235+
gen_icon="⏭️"
236+
gen_cached=$((gen_cached + 1))
237+
else
238+
gen_icon=""
239+
gen_fail=$((gen_fail + 1))
240+
fi
241+
242+
# Run build only if generate succeeded
243+
build_icon="⏭️"
244+
build_note="skipped"
245+
build_ec=-1
246+
scenario_out="$workdir/${stem}_scenario.yml"
247+
if [[ $gen_ec -eq 0 || $gen_ec -eq 100 ]]; then
248+
# Run build writing scenario into workdir via -o
249+
("${TOPGEN_INVOKE[@]}" build "$cfg_abs" -o "$scenario_out") 2>&1 | tee "$workdir/build.log"
250+
build_ec=${PIPESTATUS[0]}
251+
case "$build_ec" in
252+
0)
253+
build_icon=""; build_note="ok"; build_ok=$((build_ok + 1));;
254+
3)
255+
build_icon="⚠️"; build_note="validation failed"; build_validation=$((build_validation + 1));;
256+
2)
257+
build_icon=""; build_note="config error"; build_config=$((build_config + 1));;
258+
1)
259+
build_icon=""; build_note="runtime error"; build_runtime=$((build_runtime + 1));;
260+
*)
261+
build_icon=""; build_note="exit $build_ec"; build_other=$((build_other + 1));;
262+
esac
263+
:
264+
else
265+
build_skipped=$((build_skipped + 1))
266+
fi
267+
268+
# Summary line for this config
269+
gen_col="$gen_icon"
270+
if [[ $gen_ec -eq 2 ]]; then gen_col="❌ config"; fi
271+
if [[ $gen_ec -eq 100 ]]; then gen_col="⏭️ cached"; fi
272+
if [[ $gen_ec -ne 0 && $gen_ec -ne 2 && $gen_ec -ne 100 ]]; then gen_col="❌ runtime"; fi
273+
274+
build_col="$build_icon"
275+
case "$build_ec" in
276+
0) build_col="" ;;
277+
3) build_col="⚠️ validation" ;;
278+
2) build_col="❌ config" ;;
279+
1) build_col="❌ runtime" ;;
280+
-1) build_col="⏭️ skipped" ;;
281+
*) build_col="❓ other" ;;
282+
esac
283+
284+
row_names+=("$cfg_name")
285+
row_gen+=("$gen_col")
286+
row_build+=("$build_col")
287+
# Update column widths (character counts)
288+
name_len=$(printf %s "$cfg_name" | wc -m | tr -d ' ')
289+
gen_len=$(printf %s "$gen_col" | wc -m | tr -d ' ')
290+
build_len=$(printf %s "$build_col" | wc -m | tr -d ' ')
291+
(( name_len > w_name )) && w_name=$name_len
292+
(( gen_len > w_gen )) && w_gen=$gen_len
293+
(( build_len > w_build )) && w_build=$build_len
294+
295+
echo
296+
done < <(find "$CONFIGS_DIR" -maxdepth 1 -type f \( -name '*.yml' -o -name '*.yaml' \) -print0)
297+
298+
if [[ $total -eq 0 ]]; then
299+
echo "⚠️ No YAML config files matched in $CONFIGS_DIR" >&2
300+
echo " Include: ${INCLUDE_PATTERNS[*]:-(none)}" >&2
301+
echo " Exclude: ${EXCLUDE_PATTERNS[*]:-(none)}" >&2
302+
exit 2
303+
fi
304+
305+
echo "======================"
306+
echo "📋 Summary"
307+
printf "%-*s %-*s %-*s\n" "$w_name" "Config" "$w_gen" "Generate" "$w_build" "Build"
308+
dash_len=$((w_name + 2 + w_gen + 2 + w_build))
309+
printf '%*s\n' "$dash_len" '' | tr ' ' '-'
310+
for i in "${!row_names[@]}"; do
311+
printf "%-*s %-*s %-*s\n" "$w_name" "${row_names[$i]}" "$w_gen" "${row_gen[$i]}" "$w_build" "${row_build[$i]}"
312+
done
313+
echo "----------------------"
314+
echo "🧮 Totals: $total configs"
315+
echo " • Generate: ✅ $gen_ok | ⏭️ $gen_cached (cached) | ❌ $gen_fail (runtime) | ❌ $gen_config (config)"
316+
echo " • Build: ✅ $build_ok | ⚠️ $build_validation (validation) | ❌ $build_runtime (runtime) | ❌ $build_config (config) | ⏭️ $build_skipped (skipped) | ❓ $build_other (other)"
317+
echo "======================"
318+
319+
echo "Done. ✨"

0 commit comments

Comments
 (0)