en fr

nyctergatis.com

Universal build for Mac

Here are simple steps you can follow to build a universal command-line program in the Terminal, outside Xcode. We'll use make, clang to compile and link source code, and lipo to merge programs compiled separately for x86 and arm64 (M1 to M4) into a single file.

We assume that you have installed Xcode, available for free on the Apple Store, and opened it at least once. The first time you launch it, you should be asked to install the development tools. We also assume that you know how to use the Terminal and have a basic knowledge of the basic Unix development tools such as gcc and make.

Compiling a universal program is made in three steps:

You don't need to specify the native target architecture of the Mac where you compile your program, but there is no harm in doing so.

In the simplest case with a single source file, program.c could be compiled as follows:

clang -o program.x64 -target x86_64-apple-macos13.0 program.c
clang -o program.a64 -target arm64-apple-macos13.0 program.c
lipo -create -output program program.x64 program.a64

For larger programs, we propose the following generic makefile, with rules to compile separately source code to each architecture: universal.mk

CC = clang
CXX = clang++
CFLAGS_X64 = -target x86_64-apple-macos13.0
CFLAGS_A64 = -target arm64-apple-macos13.0

$(TARGET): $(TARGET).universal
    mv $< $@

$(TARGET).x64: $(SOURCE:.c=.x64.o)
$(TARGET).a64: $(SOURCE:.c=.a64.o)

%.universal: %.x64 %.a64
    lipo -create -output $@ $^

%.x64:
    $(CC) $(CFLAGS_X64) -o $@ $^

%.a64:
    $(CC) $(CFLAGS_A64) -o $@ $^

%.x64.o: %.c
    $(CC) $(CFLAGS) $(CFLAGS_X64) -c -o $@ $<

%.a64.o: %.c
    $(CC) $(CFLAGS) $(CFLAGS_A64) -c -o $@ $<

%.x64.o: %.cpp
    $(CXX) $(CXXFLAGS) $(CFLAGS_X64) -c -o $@ $<

%.a64.o: %.cpp
    $(CXX) $(CXXFLAGS) $(CFLAGS_A64) -c -o $@ $<

.PHONY: clean
clean:
    rm -f *.x64.o *.a64.o *.x64 *.a64

To use it with your files file1.c, file2.c, etc., write your own makefile and include it:

  SOURCE = file1.c file2.c file3.c
  TARGET = program

  # define vpath, VPATH, CFLAGS etc. as usual

  .PHONY: main
  main: $(TARGET)

  include universal.mk

Typing make will: