@@ -48,6 +48,20 @@ if [ ! -f "$PTOAS_BIN" ]; then
4848 exit 1
4949fi
5050
51+ detect_ptoas_version () {
52+ local cache_file=" ${PTO_SOURCE_DIR} /build/CMakeCache.txt"
53+ local version=" "
54+ if [[ -f " ${cache_file} " ]]; then
55+ version=" $( awk -F= ' /^PTOAS_RELEASE_VERSION_OVERRIDE:STRING=/{print $2}' " ${cache_file} " ) "
56+ if [[ -z " ${version} " ]]; then
57+ version=" $( awk -F= ' /^CMAKE_PROJECT_VERSION:STATIC=/{print $2}' " ${cache_file} " ) "
58+ fi
59+ fi
60+ printf ' %s' " ${version} "
61+ }
62+
63+ PTOAS_WRAPPER_VERSION=" $( detect_ptoas_version) "
64+
5165mkdir -p " ${PTOAS_DIST_DIR} /bin" " ${PTOAS_DEPS_DIR} "
5266cp -fL " $PTOAS_BIN " " ${PTOAS_DIST_DIR} /bin/"
5367chmod +x " ${PTOAS_DIST_DIR} /bin/ptoas"
@@ -233,12 +247,110 @@ for target in iter_targets():
233247PY
234248}
235249
250+ strip_nonportable_rpaths () {
251+ python3 - " ${PTOAS_DIST_DIR} " << 'PY '
252+ import os
253+ import subprocess
254+ import sys
255+ from pathlib import Path
256+
257+ root = Path(sys.argv[1]).resolve()
258+ allowed_prefixes = (
259+ "@loader_path/",
260+ "@executable_path/",
261+ )
262+
263+
264+ def iter_targets():
265+ for base, _, files in os.walk(root):
266+ for name in sorted(files):
267+ if name == "ptoas" or name.endswith(".dylib"):
268+ yield Path(base, name).resolve()
269+
270+
271+ def iter_rpaths(target: Path):
272+ output = subprocess.check_output(
273+ ["otool", "-l", str(target)],
274+ stderr=subprocess.STDOUT,
275+ text=True,
276+ )
277+ want = False
278+ for line in output.splitlines():
279+ fields = line.strip().split()
280+ if len(fields) >= 2 and fields[0] == "cmd" and fields[1] == "LC_RPATH":
281+ want = True
282+ continue
283+ if want and len(fields) >= 2 and fields[0] == "path":
284+ yield fields[1]
285+ want = False
286+
287+
288+ for target in iter_targets():
289+ for rpath in list(iter_rpaths(target)):
290+ if rpath.startswith(allowed_prefixes):
291+ continue
292+ print(f"delete non-portable rpath: {target} :: {rpath}")
293+ subprocess.check_call(
294+ ["install_name_tool", "-delete_rpath", rpath, str(target)]
295+ )
296+ PY
297+ }
298+
299+ validate_packaged_ptoas_surface () {
300+ python3 - " ${PTOAS_DIST_DIR} /bin/ptoas" << 'PY '
301+ import fnmatch
302+ import os
303+ import subprocess
304+ import sys
305+ from pathlib import Path
306+
307+ target = Path(sys.argv[1]).resolve()
308+ denylist = (
309+ "libMLIRMlirOptMain.dylib",
310+ "libMLIROptLib.dylib",
311+ "libMLIR*Test*.dylib",
312+ "libMLIR*TestPasses*.dylib",
313+ )
314+
315+ output = subprocess.check_output(
316+ ["otool", "-L", str(target)],
317+ stderr=subprocess.STDOUT,
318+ text=True,
319+ )
320+ deps = []
321+ for line in output.splitlines()[1:]:
322+ dep = line.strip().split(" ", 1)[0]
323+ if dep:
324+ deps.append(os.path.basename(dep))
325+
326+ bad = sorted(
327+ {
328+ dep
329+ for dep in deps
330+ if any(fnmatch.fnmatch(dep, pattern) for pattern in denylist)
331+ }
332+ )
333+ if bad:
334+ for dep in bad:
335+ print(
336+ f"ERROR: packaged ptoas still links forbidden cold-start dependency: {dep}",
337+ file=sys.stderr,
338+ )
339+ sys.exit(1)
340+
341+ print(f"ptoas direct dylib dependency count: {len(deps)}")
342+ PY
343+ }
344+
236345echo " Collecting dylib dependencies..."
237346collect_dylibs " ${PTOAS_DIST_DIR} /bin/ptoas"
238347
239348echo " Rewriting packaged install names..."
240349rewrite_packaged_install_names
241350
351+ echo " Stripping non-portable rpaths..."
352+ strip_nonportable_rpaths
353+
242354echo " Validating packaged dependency install names..."
243355if ! python3 - " ${PTOAS_DIST_DIR} " << 'PY '
244356import os
288400 exit 1
289401fi
290402
403+ echo " Validating packaged ptoas dependency surface..."
404+ validate_packaged_ptoas_surface
405+
291406if ! command -v codesign > /dev/null 2>&1 ; then
292407 echo " Error: codesign is required on macOS to sign packaged artifacts" >&2
293408 exit 1
@@ -307,16 +422,20 @@ done
307422shopt -u nullglob
308423
309424echo " Creating wrapper script..."
310- cat > " ${PTOAS_DIST_DIR} /ptoas" << ' WRAPPER_EOF '
425+ cat > " ${PTOAS_DIST_DIR} /ptoas" << WRAPPER_EOF
311426#!/bin/bash
312- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
313- export DYLD_LIBRARY_PATH="${SCRIPT_DIR}/lib:${DYLD_LIBRARY_PATH}"
314- exec "${SCRIPT_DIR}/bin/ptoas" "$@"
427+ SCRIPT_DIR="\$ (cd "\$ (dirname "\$ {BASH_SOURCE[0]}")" && pwd)"
428+ if [[ \$ # -eq 1 && "\$ {1:-}" == "--version" && -n "${PTOAS_WRAPPER_VERSION} " ]]; then
429+ printf 'ptoas %s\n' "${PTOAS_WRAPPER_VERSION} "
430+ exit 0
431+ fi
432+ export DYLD_LIBRARY_PATH="\$ {SCRIPT_DIR}/lib:\$ {DYLD_LIBRARY_PATH}"
433+ exec "\$ {SCRIPT_DIR}/bin/ptoas" "\$ @"
315434WRAPPER_EOF
316435chmod +x " ${PTOAS_DIST_DIR} /ptoas"
317436
318437echo " Smoke testing packaged ptoas dist..."
319- env -u DYLD_LIBRARY_PATH -u LD_LIBRARY_PATH " ${PTOAS_DIST_DIR} /ptoas" --version
438+ env -u DYLD_LIBRARY_PATH -u LD_LIBRARY_PATH " ${PTOAS_DIST_DIR} /bin/ ptoas" --version > /dev/null
320439env -u DYLD_LIBRARY_PATH -u LD_LIBRARY_PATH \
321440 " ${PTOAS_DIST_DIR} /ptoas" \
322441 " ${PTO_SOURCE_DIR} /test/basic/kernel_kind_vector_scf_while_emitc.pto" \
0 commit comments