Skip to content

Commit 312b4ff

Browse files
committed
fix(ios): prefer native CocoaPods before Rosetta
1 parent e157c9f commit 312b4ff

2 files changed

Lines changed: 79 additions & 21 deletions

File tree

‎lib/services/cocoapods-service.ts‎

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -57,32 +57,27 @@ export class CocoaPodsService implements ICocoaPodsService {
5757

5858
public async executePodInstall(
5959
projectRoot: string,
60-
xcodeProjPath: string
60+
xcodeProjPath: string,
6161
): Promise<ISpawnResult> {
6262
this.$logger.info("Installing pods...");
6363
let podTool = this.$config.USE_POD_SANDBOX ? "sandbox-pod" : "pod";
6464
const args = ["install"];
6565

6666
if (process.platform === "darwin" && process.arch === "arm64") {
67-
// check if pod is installed as an x86_64 binary or a native arm64 one
68-
// we run the following:
69-
// arch -x86_64 pod --version
70-
// if it's an arm64 binary, we'll get something like this as a result:
71-
// arch: posix_spawnp: pod: Bad CPU type in executable
72-
// in which case, we should run it natively.
73-
const res: string = await this.$childProcess
74-
.exec("arch -x86_64 pod --version", null, {
67+
try {
68+
await this.$childProcess.exec(`${podTool} --version`, null, {
7569
showStderr: true,
76-
})
77-
.then((res) => res.stdout + " " + res.stderr)
78-
.catch((err) => err.message);
79-
80-
if (!res.includes("Bad CPU type in executable")) {
70+
});
71+
} catch (err) {
72+
if (
73+
!/Bad CPU type in executable|Exec format error/i.test(err.message)
74+
) {
75+
throw err;
76+
}
8177
this.$logger.trace(
82-
"Running on arm64 but pod is installed under rosetta2 - running pod through rosetta2"
78+
"Native pod execution is unavailable - running pod through rosetta2",
8379
);
84-
args.unshift(podTool);
85-
args.unshift("-x86_64");
80+
args.unshift("-x86_64", podTool);
8681
podTool = "arch";
8782
}
8883
}
@@ -92,7 +87,7 @@ export class CocoaPodsService implements ICocoaPodsService {
9287
args,
9388
"close",
9489
{ cwd: projectRoot, stdio: ["pipe", process.stdout, process.stdout] },
95-
{ throwError: false }
90+
{ throwError: false },
9691
);
9792

9893
if (podInstallResult.exitCode !== 0) {

‎test/cocoapods-service.ts‎

Lines changed: 66 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -820,10 +820,10 @@ end`,
820820
options?: any,
821821
execOptions?: IExecOptions
822822
): Promise<any> => {
823-
if (command === "arch -x86_64 pod --version") {
824-
// This is the command that is used to check if cocoapods is installed under Rosetta 2
823+
if (command.endsWith("pod --version")) {
824+
// Native CocoaPods is available.
825825
return {
826-
stdout: "Bad CPU type in executable",
826+
stdout: "1.17.0",
827827
stderr: "",
828828
exitCode: 0,
829829
};
@@ -872,6 +872,69 @@ end`,
872872
});
873873
});
874874

875+
describe("on Apple Silicon", () => {
876+
let platform: PropertyDescriptor;
877+
let arch: PropertyDescriptor;
878+
beforeEach(() => {
879+
platform = Object.getOwnPropertyDescriptor(process, "platform");
880+
arch = Object.getOwnPropertyDescriptor(process, "arch");
881+
Object.defineProperty(process, "platform", { value: "darwin" });
882+
Object.defineProperty(process, "arch", { value: "arm64" });
883+
});
884+
afterEach(() => {
885+
Object.defineProperty(process, "platform", platform);
886+
Object.defineProperty(process, "arch", arch);
887+
});
888+
["pod", "sandbox-pod"].forEach((podTool) => {
889+
[false, true].forEach((needsRosetta) => {
890+
it(`uses ${podTool} ${needsRosetta ? "through Rosetta after a native CPU failure" : "natively even when Rosetta is installed"}`, async () => {
891+
testInjector.resolve<IConfiguration>("config").USE_POD_SANDBOX =
892+
podTool === "sandbox-pod";
893+
const childProcess =
894+
testInjector.resolve<IChildProcess>("childProcess");
895+
const probes: string[] = [];
896+
childProcess.exec = async (command: string): Promise<any> => {
897+
probes.push(command);
898+
if (needsRosetta) {
899+
throw new Error("Bad CPU type in executable");
900+
}
901+
return { stdout: "1.17.0", stderr: "", exitCode: 0 };
902+
};
903+
childProcess.spawnFromEvent = async (
904+
command: string,
905+
args: string[],
906+
): Promise<ISpawnResult> => {
907+
assert.equal(command, needsRosetta ? "arch" : podTool);
908+
assert.deepStrictEqual(
909+
args,
910+
needsRosetta ? ["-x86_64", podTool, "install"] : ["install"],
911+
);
912+
return { stdout: "", stderr: "", exitCode: 0 };
913+
};
914+
await cocoapodsService.executePodInstall(
915+
projectRoot,
916+
xcodeProjPath,
917+
);
918+
assert.deepStrictEqual(probes, [`${podTool} --version`]);
919+
});
920+
});
921+
});
922+
it("preserves unrelated native probe failures", async () => {
923+
const childProcess =
924+
testInjector.resolve<IChildProcess>("childProcess");
925+
childProcess.exec = async (): Promise<any> => {
926+
throw new Error("pod: command not found");
927+
};
928+
childProcess.spawnFromEvent = async (): Promise<ISpawnResult> => {
929+
throw new Error("unexpected Rosetta fallback");
930+
};
931+
await assert.isRejected(
932+
cocoapodsService.executePodInstall(projectRoot, xcodeProjPath),
933+
"pod: command not found",
934+
);
935+
});
936+
});
937+
875938
it("calls pod install spawnFromEvent with correct arguments", async () => {
876939
const childProcess = testInjector.resolve<IChildProcess>("childProcess");
877940
let commandCalled = "";

0 commit comments

Comments
 (0)