Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions MacPassHTTP.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@
buildSettings = {
BUNDLE_LOADER = "";
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 0.3.2;
CURRENT_PROJECT_VERSION = 0.3.3;
DEPLOYMENT_LOCATION = YES;
DSTROOT = "$(HOME)";
FRAMEWORK_SEARCH_PATHS = (
Expand Down Expand Up @@ -400,7 +400,7 @@
buildSettings = {
BUNDLE_LOADER = "";
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 0.3.2;
CURRENT_PROJECT_VERSION = 0.3.3;
DEPLOYMENT_LOCATION = YES;
DSTROOT = "$(HOME)";
FRAMEWORK_SEARCH_PATHS = (
Expand Down
6 changes: 6 additions & 0 deletions MacPassHTTP/Base.lproj/MacPassHTTPSettings.xib
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="1b8-d7-7dw" secondAttribute="trailing" constant="20" symbolic="YES" id="yfk-sZ-Xye"/>
</constraints>
</view>
<constraints>
<constraint firstAttribute="height" constant="168" id="ADv-hg-ht1"/>
</constraints>
</box>
<box borderType="line" title="Interface" translatesAutoresizingMaskIntoConstraints="NO" id="91g-f4-0PT">
<rect key="frame" x="17" y="293" width="248" height="90"/>
Expand Down Expand Up @@ -120,6 +123,9 @@
<constraint firstItem="7X4-aF-1sY" firstAttribute="top" secondItem="Ph9-8D-46q" secondAttribute="bottom" constant="6" symbolic="YES" id="s0h-9r-tOI"/>
</constraints>
</view>
<constraints>
<constraint firstAttribute="height" constant="90" id="IFc-hg-ht1"/>
</constraints>
</box>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="BOb-dd-2DX">
<rect key="frame" x="20" y="78" width="242" height="25"/>
Expand Down
84 changes: 75 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,29 @@ Download the latest release from the [Releases page](https://github.com/MacPass/

### Building from source

The dependencies and the MacPass plugin SDK predate current Xcode, so a plain
`carthage bootstrap` + `xcodebuild` no longer works out of the box. Two helper
scripts encapsulate the required workarounds (explained in the notes below):

* `build.sh` — **full build**: builds MacPass's dependencies, MacPassHTTP's
dependencies, then compiles and installs the plugin.
* `build-deps.sh` — just MacPassHTTP's own Carthage dependencies.

#### Quick build (recommended)

```bash
git clone https://github.com/MacPass/MacPassHTTP
git clone https://github.com/mstarke/MacPass # sibling checkout, see layout below
cd MacPassHTTP
./build.sh
```

This installs the plugin to `~/Library/Application Support/MacPass/MacPassHTTP.mpplugin`
(arm64; restart MacPass to load it). The build is arm64-only, matching MacPass
running natively on Apple Silicon.

#### Manual steps

* Clone the repository
```bash
git clone https://github.com/MacPass/MacPassHTTP
Expand All @@ -24,17 +47,50 @@ cd MacPassHTTP
* Install [Carthage](https://github.com/Carthage/Carthage#installing-carthage)
* Fetch and build dependencies for MacPassHTTP
```bash
carthage bootstrap --platform macOS
./build-deps.sh
```
* Clone MacPass and fetch and build dependencies

> **Note:** Use `./build-deps.sh` instead of calling `carthage bootstrap` directly.
> The pinned dependencies (GCDWebServer 3.4.1, JSONModel, KeePassHTTPKit) date from
> ~2019 and need two workarounds on modern Xcode, both handled by the script:
>
> 1. **Deployment targets** (macOS 10.7 / iOS 8.0) are below what modern Xcode
> supports, so linking fails with `SDK does not contain 'libarclite'` (Apple
> removed `libarclite_*.a` in Xcode 14.3+). The script injects
> `carthage-deployment-target.xcconfig` via `XCODE_XCCONFIG_FILE`, raising the
> targets to the supported minimums and pinning `ARCHS = arm64`.
> 2. **Duplicate schemes:** GCDWebServer and JSONModel each ship several shared
> schemes that build a framework with the *same* product name (e.g. both
> `JSONModel` and `JSONModel-mac` produce `JSONModel.framework`). Carthage
> builds them in parallel and they race to write the same path in
> `Carthage/Build/Mac`, so you intermittently get an iOS framework
> (`building for macOS, but linking in dylib built for iOS`) or an arm64-only
> iOS slice where the macOS framework should be. The script splits the build
> into `checkout → prune → build`, deleting the duplicate non-macOS schemes so
> exactly one scheme per framework remains.
>
> The build is arm64-only (matches MacPass running natively on Apple Silicon).
> The script forwards arguments to `carthage`, e.g. `./build-deps.sh update`.
> Editing the dependency Xcode projects directly does not stick — `carthage`
> re-checks-out and overwrites them on every run, which is why the fixes live in
> the script and xcconfig.
* Clone MacPass (as a sibling directory) and build the dependencies the plugin
needs. On modern Xcode the non-macOS schemes must be pruned first, and
TransformerKit fails to compile (removed Darwin `xlocale` module) but isn't
needed by the plugin, so build only the required dependencies:
```bash
cd ..
git clone https://github.com/mstarke/MacPass
cd MacPass
git checkout 0.7.4
git submodule update --init --recursive
carthage bootstrap --platform macOS
carthage checkout
# remove iOS/tvOS/watchOS schemes so Carthage doesn't race same-named frameworks
find Carthage/Checkouts -path "*/xcshareddata/xcschemes/*.xcscheme" \
\( -iname "*iOS*" -o -iname "*tvOS*" -o -iname "*watchOS*" \) -delete
XCODE_XCCONFIG_FILE="$(pwd)/../MacPassHTTP/carthage-deployment-target.xcconfig" \
carthage build HNHUi KeePassKit KissXML --platform macOS
```
(`build.sh` does all of the above for you.)

* If your folder structure isn't like the following, you need to adjust the ````HEADER_SEARCH_PATHS```` to point to the MacPass folder
````
Expand All @@ -43,15 +99,25 @@ carthage bootstrap --platform macOS
└─ MacPassHTTP
````

* Change back to the MacPassHTTP folder, compile and install
* Change back to the MacPassHTTP folder, compile and install. The plugin's own
deployment target (10.10) is too low for modern Xcode, it must build arm64 to
match the dependencies, and it needs MacPass's built frameworks on the
framework search path:
```bash
cd ..
cd MacPassHTTP
xcodebuild
cd ../MacPassHTTP
XCODE_XCCONFIG_FILE="$(pwd)/carthage-deployment-target.xcconfig" xcodebuild \
-scheme MacPassHTTP -configuration Release \
MACOSX_DEPLOYMENT_TARGET=10.13 ARCHS=arm64 ONLY_ACTIVE_ARCH=NO \
"FRAMEWORK_SEARCH_PATHS=\$(inherited) \$(PROJECT_DIR)/../MacPass/Carthage/Build/Mac" \
CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES=YES
```
After the build, KeePassHTTPKit's own dependencies (GCDWebServers, JSONModel)
must be embedded into the installed plugin so it can load — `build.sh` does
this automatically. Again, prefer `./build.sh` over running these by hand.

The Plugin is moved to the plugin folder of MacPass automacially.
The plugin is installed automatically to MacPass's plugin folder:
````~/Library/Application Support/MacPass/MacPassHTTP.mpplugin````
Restart MacPass to load it.

## License

Expand Down
88 changes: 88 additions & 0 deletions build-deps.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
#!/usr/bin/env bash
#
# build-deps.sh — fetch and build MacPassHTTP's Carthage dependencies.
#
# Two problems make a plain `carthage bootstrap --platform macOS` fail with the
# pinned, ~2019-era dependencies on modern Xcode:
#
# 1. Deployment targets (macOS 10.7 / iOS 8.0) are below what modern Xcode
# supports, so the link step fails with "SDK does not contain 'libarclite'"
# (Apple removed libarclite_*.a in Xcode 14.3+). Fixed by
# carthage-deployment-target.xcconfig, injected via XCODE_XCCONFIG_FILE,
# which raises the targets to the supported minimums and pins ARCHS.
#
# 2. GCDWebServer and JSONModel each ship several shared schemes that build a
# framework with the SAME product name (e.g. both "JSONModel" and
# "JSONModel-mac" produce JSONModel.framework; "GCDWebServers (iOS)" and
# "GCDWebServers (Mac)" produce GCDWebServers.framework). Carthage builds all
# macOS-eligible schemes in parallel and they race to write the same path in
# Carthage/Build/Mac. Depending on which build wins the race you intermittently
# get an iOS framework ("building for macOS, but linking in dylib built for
# iOS") or an arm64-only iOS-device slice (arch-mismatch link errors) in place
# of the real macOS framework. We split bootstrap into checkout -> prune -> build
# and delete the duplicate non-macOS schemes so exactly one scheme per
# framework remains, making the build deterministic.
#
# Usage:
# ./build-deps.sh # checkout, prune duplicate schemes, build (macOS)
# ./build-deps.sh update # carthage update (re-resolve), then prune + build
# ./build-deps.sh <args...> # forwarded verbatim to carthage (with xcconfig)

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
XCCONFIG="$SCRIPT_DIR/carthage-deployment-target.xcconfig"
CHECKOUTS="$SCRIPT_DIR/Carthage/Checkouts"

if ! command -v carthage >/dev/null 2>&1; then
echo "error: carthage not found. Install it: https://github.com/Carthage/Carthage#installing-carthage" >&2
exit 1
fi
if [ ! -f "$XCCONFIG" ]; then
echo "error: missing $XCCONFIG" >&2
exit 1
fi

run_carthage() {
echo "==> XCODE_XCCONFIG_FILE=$XCCONFIG"
echo "==> carthage $*"
XCODE_XCCONFIG_FILE="$XCCONFIG" carthage "$@"
}

# Keep exactly one macOS scheme per framework so Carthage's parallel builds can't
# race two same-named frameworks into Carthage/Build/Mac. Safe to re-run; missing
# files are ignored.
prune_duplicate_schemes() {
echo "==> pruning duplicate non-macOS schemes from checkouts"
local removed=0 f
# JSONModel: keep JSONModel-mac, drop the iOS/tvOS/watchOS schemes.
for f in "JSONModel" "JSONModel-tvOS" "JSONModel-watchOS"; do
local p="$CHECKOUTS/jsonmodel/JSONModel.xcodeproj/xcshareddata/xcschemes/$f.xcscheme"
if [ -f "$p" ]; then rm -f "$p"; echo " removed jsonmodel scheme: $f"; removed=$((removed+1)); fi
done
# GCDWebServer: keep "GCDWebServers (Mac)", drop iOS/tvOS.
for f in "GCDWebServers (iOS)" "GCDWebServers (tvOS)"; do
local p="$CHECKOUTS/GCDWebServer/GCDWebServer.xcodeproj/xcshareddata/xcschemes/$f.xcscheme"
if [ -f "$p" ]; then rm -f "$p"; echo " removed GCDWebServer scheme: $f"; removed=$((removed+1)); fi
done
echo " pruned $removed scheme(s)"
}

mode="${1:-bootstrap}"
case "$mode" in
bootstrap)
run_carthage checkout
prune_duplicate_schemes
run_carthage build --platform macOS
;;
update)
shift
run_carthage update --no-build "$@"
prune_duplicate_schemes
run_carthage build --platform macOS
;;
*)
# Anything else: forward verbatim (still with the xcconfig).
run_carthage "$@"
;;
esac
137 changes: 137 additions & 0 deletions build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
#!/usr/bin/env bash
#
# build.sh — full build of the MacPassHTTP plugin on modern Xcode (Apple Silicon).
#
# Orchestrates the whole chain and installs the plugin to
# ~/Library/Application Support/MacPass/MacPassHTTP.mpplugin
#
# 1. MacPass (sibling repo) dependencies — the plugin compiles against MacPass
# headers (HEADER_SEARCH_PATHS = ../MacPass/**) and links HNHUi/KeePassKit,
# so MacPass's own Carthage deps must be checked out and built first.
# 2. MacPassHTTP's own Carthage deps — via ./build-deps.sh.
# 3. The plugin itself — xcodebuild, with the overrides explained below.
# 4. Embed KeePassHTTPKit's transitive frameworks (GCDWebServers, JSONModel)
# into the installed plugin so it can load (see step 4 for why).
# 5. Code sign the bundle (inside-out) so MacPass will load it — MacPass runs
# `codesign --verify` and refuses unsigned plugins (see step 5 for why).
#
# Requirements:
# - Xcode + carthage installed.
# - MacPass checked out as a sibling directory (../MacPass). Clone with:
# git clone https://github.com/mstarke/MacPass ../MacPass
#
# Usage: ./build.sh

set -euo pipefail

ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
MACPASS="$(cd "$ROOT/.." && pwd)/MacPass"
XCCONFIG="$ROOT/carthage-deployment-target.xcconfig"
CONFIG="Release"
PLUGIN="$HOME/Library/Application Support/MacPass/MacPassHTTP.mpplugin"
# Signing identity for the final codesign pass. Defaults to ad-hoc ("-"), which
# is enough for MacPass to load the plugin (it doesn't pin a signing anchor).
# Export CODESIGN_IDENTITY="Developer ID Application: ..." for a distributable build.
SIGN_ID="${CODESIGN_IDENTITY:--}"

log() { printf '\n\033[1;34m==> %s\033[0m\n' "$*"; }
die() { printf '\033[1;31merror: %s\033[0m\n' "$*" >&2; exit 1; }

command -v carthage >/dev/null 2>&1 || die "carthage not found. See https://github.com/Carthage/Carthage#installing-carthage"
command -v xcodebuild >/dev/null 2>&1 || die "xcodebuild not found. Install Xcode and run xcode-select."
[ -f "$XCCONFIG" ] || die "missing $XCCONFIG"
[ -d "$MACPASS/.git" ] || die "MacPass not found at $MACPASS. Clone it: git clone https://github.com/mstarke/MacPass \"$MACPASS\""

# Delete shared schemes for platforms we don't build (iOS/tvOS/watchOS). These
# old projects ship several schemes that emit a same-named framework; Carthage
# builds them in parallel for --platform macOS and they race to write the same
# path in Carthage/Build/Mac, intermittently leaving an iOS or arm64-only build
# where the macOS framework should be. Removing them leaves one macOS scheme per
# framework. Re-run safe; runs again after each checkout restores the schemes.
prune_non_macos_schemes() {
local checkouts="$1"
log "pruning iOS/tvOS/watchOS schemes under $checkouts"
find "$checkouts" -path "*/xcshareddata/xcschemes/*.xcscheme" \
\( -iname "*iOS*" -o -iname "*tvOS*" -o -iname "*watchOS*" \) -print -delete || true
}

# ---------------------------------------------------------------------------
log "1/5 Building MacPass dependencies ($MACPASS)"
# Submodule (DDHotKey) + Carthage deps. We build only what the plugin needs
# (HNHUi, KeePassKit and its KissXML dep); TransformerKit fails to compile on a
# modern SDK (removed Darwin 'xlocale' module) and the plugin doesn't need it.
git -C "$MACPASS" submodule update --init --recursive
XCODE_XCCONFIG_FILE="$XCCONFIG" carthage checkout --project-directory "$MACPASS"
prune_non_macos_schemes "$MACPASS/Carthage/Checkouts"
XCODE_XCCONFIG_FILE="$XCCONFIG" carthage build HNHUi KeePassKit KissXML \
--platform macOS --project-directory "$MACPASS"

# ---------------------------------------------------------------------------
log "2/5 Building MacPassHTTP dependencies"
"$ROOT/build-deps.sh"

# ---------------------------------------------------------------------------
log "3/5 Building the MacPassHTTP plugin ($CONFIG)"
# Command-line settings (highest precedence) override the project's stale values:
# MACOSX_DEPLOYMENT_TARGET=10.13 project sets 10.10, too low -> libarclite link error
# ARCHS=arm64 match the arm64-only Carthage frameworks
# FRAMEWORK_SEARCH_PATHS += MacPass build dir, so <HNHUi/..>, <KeePassKit/..>
# resolve to the built frameworks, not source headers
# CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES=YES
# the recursive HEADER_SEARCH_PATHS=../MacPass/**
# pulls source headers into framework modules, which
# -Werror would otherwise reject
XCODE_XCCONFIG_FILE="$XCCONFIG" xcodebuild \
-project "$ROOT/MacPassHTTP.xcodeproj" -scheme MacPassHTTP -configuration "$CONFIG" \
MACOSX_DEPLOYMENT_TARGET=10.13 ARCHS=arm64 ONLY_ACTIVE_ARCH=NO \
"FRAMEWORK_SEARCH_PATHS=\$(inherited) \$(PROJECT_DIR)/../MacPass/Carthage/Build/Mac" \
CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES=YES

[ -d "$PLUGIN" ] || die "build reported success but $PLUGIN is missing"

# The project's "Versioning" build phase stamps CFBundleVersion from the git
# commit count, but on a clean build it runs before Info.plist exists in the
# output and silently no-ops, leaving the "UNKNOWN" placeholder. Stamp it here
# (same logic as the build phase: Release = commit count) so it's deterministic.
BUILD_NUMBER="$(git -C "$ROOT" rev-list --count HEAD 2>/dev/null || echo 0)"
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $BUILD_NUMBER" "$PLUGIN/Contents/Info.plist" 2>/dev/null \
|| /usr/libexec/PlistBuddy -c "Add :CFBundleVersion string $BUILD_NUMBER" "$PLUGIN/Contents/Info.plist" 2>/dev/null || true

# ---------------------------------------------------------------------------
log "4/5 Embedding transitive frameworks into the plugin"
# The project embeds only KeePassHTTPKit.framework, but it dynamically links
# GCDWebServers and JSONModel (@rpath). KeePassHTTPKit declares an rpath of
# @loader_path/Frameworks, so place its deps there; otherwise the plugin fails
# to load. HNHUi/KeePassKit are NOT embedded on purpose — they resolve from the
# host MacPass.app via @executable_path/../Frameworks.
NESTED="$PLUGIN/Contents/Frameworks/KeePassHTTPKit.framework/Versions/A/Frameworks"
mkdir -p "$NESTED"
for fw in GCDWebServers JSONModel; do
rm -rf "$NESTED/$fw.framework"
cp -R "$ROOT/Carthage/Build/Mac/$fw.framework" "$NESTED/"
done

# ---------------------------------------------------------------------------
log "5/5 Code signing the plugin (id: $SIGN_ID)"
# MacPass refuses to load plugins that fail `codesign --verify` (the UI shows
# "Plugin is not properly signed"; see MPPluginHost.m -_isSignedPluginURL:).
# xcodebuild leaves the bundle with only the arm64 linker's ad-hoc signature,
# which has no sealed resources, and the steps above (Info.plist edit, framework
# copy) would invalidate any real signature anyway — so we sign LAST, inside-out:
# nested frameworks deepest-first, then the outer bundle, which seals everything
# into Contents/_CodeSignature/CodeResources. MacPass doesn't pin a signing
# anchor, so an ad-hoc signature is accepted.
while IFS= read -r fw; do
codesign --force --sign "$SIGN_ID" "$fw" || die "failed to sign $fw"
done < <(find "$PLUGIN/Contents/Frameworks" -name "*.framework" -depth)

codesign --force --sign "$SIGN_ID" "$PLUGIN" || die "failed to sign $PLUGIN"

# Verify exactly as MacPass does before it loads the plugin.
codesign --verify --verbose "$PLUGIN" || die "codesign --verify failed for $PLUGIN"

log "Done. Installed plugin:"
echo " $PLUGIN"
echo " arch: $(lipo -info "$PLUGIN/Contents/MacOS/MacPassHTTP" 2>/dev/null | sed 's/.*: //')"
echo " version: $(/usr/libexec/PlistBuddy -c 'Print :CFBundleVersion' "$PLUGIN/Contents/Info.plist" 2>/dev/null)"
echo "Restart MacPass to load it."
Loading