Browse Source

build: add pkg-config and multi-shim linking

Eric Streit 3 days ago
parent
commit
988e9cb4d2

+ 2 - 1
README.md

@@ -69,7 +69,8 @@ tools/m2make/b_v3/M2make --separate --dialect iso \
 
 
 `M2 -c` emits one unit's QBE IR; `M2make --separate` is the transitive
 `M2 -c` emits one unit's QBE IR; `M2make --separate` is the transitive
 `.def` resolver and object linker.  The one-shot mode remains the default
 `.def` resolver and object linker.  The one-shot mode remains the default
-for the test suite and corpus.
+for the test suite and corpus.  `M2make` accepts repeated `--extra C`
+shims plus `--pkg-config pkg` and `--lib lib` for C library linking.
 
 
 Documentation PDFs are generated with Pandoc:
 Documentation PDFs are generated with Pandoc:
 
 

+ 130 - 14
tools/m2make/M2make.mod

@@ -1,7 +1,8 @@
 MODULE M2make;
 MODULE M2make;
 (* M2make: build-order front-end for the M2 and GNU Modula-2 compilers.
 (* M2make: build-order front-end for the M2 and GNU Modula-2 compilers.
    Usage: M2make [-n] [-v] [-c m2|gm2] [-I dir] [-o out]
    Usage: M2make [-n] [-v] [-c m2|gm2] [-I dir] [-o out]
-                 [--m2bin path] [--shim path] main.mod
+                 [--m2bin path] [--shim path] [--extra path]...
+                 [--pkg-config pkg]... [--lib lib]... main.mod
    Follows IMPORT/FROM imports from main.mod transitively, sorts the
    Follows IMPORT/FROM imports from main.mod transitively, sorts the
    modules topologically, skips the build when the output is newer
    modules topologically, skips the build when the output is newer
    than every source, otherwise invokes the selected compiler.
    than every source, otherwise invokes the selected compiler.
@@ -20,7 +21,8 @@ CONST
   MaxMods = 256;
   MaxMods = 256;
   MaxDeps = 64;
   MaxDeps = 64;
   MaxDirs = 16;
   MaxDirs = 16;
-  MaxArgs = 64;
+  MaxArgs = 128;
+  MaxExtras = 16;
   StackCap = 264;
   StackCap = 264;
   NameStride = 64;
   NameStride = 64;
   PathStride = 256;
   PathStride = 256;
@@ -29,7 +31,7 @@ TYPE
   Name = ARRAY [0..63] OF CHAR;
   Name = ARRAY [0..63] OF CHAR;
   Path = ARRAY [0..255] OF CHAR;
   Path = ARRAY [0..255] OF CHAR;
   Line = ARRAY [0..2047] OF CHAR;
   Line = ARRAY [0..2047] OF CHAR;
-  Cmd = ARRAY [0..2047] OF CHAR;
+  Cmd = ARRAY [0..4095] OF CHAR;
 
 
 VAR
 VAR
   nMods: CARDINAL;
   nMods: CARDINAL;
@@ -56,7 +58,10 @@ VAR
   mainPath, outName, m2bin, shimPath: Path;
   mainPath, outName, m2bin, shimPath: Path;
   m2Flags: Cmd;
   m2Flags: Cmd;
   dialect: Name;
   dialect: Name;
-  extraC: Path;  (* extra C file linked in m2 mode (may be empty) *)
+  extraPool: ARRAY [0 .. MaxExtras * PathStride - 1] OF CHAR;
+  pkgPool: ARRAY [0 .. MaxExtras * PathStride - 1] OF CHAR;
+  libPool: ARRAY [0 .. MaxExtras * PathStride - 1] OF CHAR;
+  nExtras, nPkgs, nLibs: CARDINAL;
   nDirs: CARDINAL;
   nDirs: CARDINAL;
   progName: Name;
   progName: Name;
   wantProg: BOOLEAN;
   wantProg: BOOLEAN;
@@ -257,6 +262,28 @@ BEGIN
   END
   END
 END PutDir;
 END PutDir;
 
 
+PROCEDURE GetPool(VAR pool: ARRAY OF CHAR; idx: CARDINAL;
+                  VAR dst: Path);
+VAR k: CARDINAL;
+BEGIN
+  k := 0;
+  WHILE k <= HIGH(dst) DO
+    dst[k] := pool[idx * PathStride + k];
+    INC(k)
+  END
+END GetPool;
+
+PROCEDURE PutPool(VAR pool: ARRAY OF CHAR; idx: CARDINAL;
+                  src: ARRAY OF CHAR);
+VAR k: CARDINAL;
+BEGIN
+  k := 0;
+  WHILE k <= HIGH(src) DO
+    pool[idx * PathStride + k] := src[k];
+    INC(k)
+  END
+END PutPool;
+
 (* ---------------- console output ---------------- *)
 (* ---------------- console output ---------------- *)
 
 
 PROCEDURE Out(s: ARRAY OF CHAR);
 PROCEDURE Out(s: ARRAY OF CHAR);
@@ -291,8 +318,9 @@ END Die;
 PROCEDURE Usage;
 PROCEDURE Usage;
 BEGIN
 BEGIN
   Out("Usage: M2make [-n] [-v] [-c m2|gm2] [-I dir] [-o out]"); OutLn;
   Out("Usage: M2make [-n] [-v] [-c m2|gm2] [-I dir] [-o out]"); OutLn;
-  Out("       [--m2bin path] [--shim path] [--extra path]
-       [--m2flag flag] [--dialect pim|iso|gm2] [--separate] main.mod"); OutLn
+  Out("       [--m2bin path] [--shim path] [--extra path]..."); OutLn;
+  Out("       [--pkg-config pkg]... [--lib lib]..."); OutLn;
+  Out("       [--m2flag flag] [--dialect pim|iso|gm2] [--separate] main.mod"); OutLn
 END Usage;
 END Usage;
 
 
 (* ---------------- module table ---------------- *)
 (* ---------------- module table ---------------- *)
@@ -705,6 +733,13 @@ BEGIN
     IF SrcNewer(tmpP, outT) THEN RETURN TRUE END;
     IF SrcNewer(tmpP, outT) THEN RETURN TRUE END;
     INC(k)
     INC(k)
   END;
   END;
+  IF SrcNewer(shimPath, outT) THEN RETURN TRUE END;
+  k := 0;
+  WHILE k < nExtras DO
+    GetPool(extraPool, k, tmpP);
+    IF SrcNewer(tmpP, outT) THEN RETURN TRUE END;
+    INC(k)
+  END;
   RETURN FALSE
   RETURN FALSE
 END NeedBuild;
 END NeedBuild;
 
 
@@ -820,6 +855,51 @@ BEGIN
   Copy(segA, cmd)
   Copy(segA, cmd)
 END AppendSeg;
 END AppendSeg;
 
 
+PROCEDURE AppendExtras(VAR cmd: ARRAY OF CHAR);
+VAR k: CARDINAL;
+BEGIN
+  k := 0;
+  WHILE k < nExtras DO
+    GetPool(extraPool, k, tmpP);
+    IF Len(tmpP) > 0 THEN AppendTok(cmd, tmpP) END;
+    INC(k)
+  END
+END AppendExtras;
+
+PROCEDURE AppendPkgConfigs(VAR cmd: ARRAY OF CHAR);
+VAR k: CARDINAL;
+BEGIN
+  k := 0;
+  WHILE k < nPkgs DO
+    GetPool(pkgPool, k, tmpP);
+    IF Len(tmpP) > 0 THEN
+      AppendSeg(cmd, " $(pkg-config --cflags --libs ");
+      AppendSeg(cmd, tmpP);
+      AppendSeg(cmd, ")")
+    END;
+    INC(k)
+  END
+END AppendPkgConfigs;
+
+PROCEDURE AppendLibs(VAR cmd: ARRAY OF CHAR);
+VAR k: CARDINAL;
+BEGIN
+  k := 0;
+  WHILE k < nLibs DO
+    GetPool(libPool, k, tmpP);
+    IF Len(tmpP) > 0 THEN
+      AppendSeg(cmd, " ");
+      IF (tmpP[0] = "-") OR (tmpP[0] = "/") THEN
+        AppendSeg(cmd, tmpP)
+      ELSE
+        AppendSeg(cmd, "-l");
+        AppendSeg(cmd, tmpP)
+      END
+    END;
+    INC(k)
+  END
+END AppendLibs;
+
 PROCEDURE BuildM2(out: ARRAY OF CHAR);
 PROCEDURE BuildM2(out: ARRAY OF CHAR);
 VAR cmd: Cmd; k: CARDINAL; m: CARDINAL; rc: INTEGER;
 VAR cmd: Cmd; k: CARDINAL; m: CARDINAL; rc: INTEGER;
 BEGIN
 BEGIN
@@ -849,7 +929,9 @@ BEGIN
   AppendSeg(cmd, progName);
   AppendSeg(cmd, progName);
   AppendSeg(cmd, ".s ");
   AppendSeg(cmd, ".s ");
   AppendTok(cmd, shimPath);
   AppendTok(cmd, shimPath);
-  IF Len(extraC) > 0 THEN AppendTok(cmd, extraC) END;
+  AppendExtras(cmd);
+  AppendPkgConfigs(cmd);
+  AppendLibs(cmd);
   AppendSeg(cmd, " -o ");
   AppendSeg(cmd, " -o ");
   AppendTok(cmd, out);
   AppendTok(cmd, out);
   AppendSeg(cmd, " -lm");
   AppendSeg(cmd, " -lm");
@@ -904,9 +986,10 @@ END SepObjStale;
 PROCEDURE AbiStampOK (): BOOLEAN;
 PROCEDURE AbiStampOK (): BOOLEAN;
 (* Content check for the object ABI/flags stamp.  This is deliberately
 (* Content check for the object ABI/flags stamp.  This is deliberately
    a small build-system marker, not a per-object binary format. *)
    a small build-system marker, not a per-object binary format. *)
-VAR h: M2makeOS.RdFile; line: Line; want: Path; ok: BOOLEAN;
+VAR h: M2makeOS.RdFile; line: Line; want, stamp: Path; ok: BOOLEAN;
 BEGIN
 BEGIN
-  h := M2makeOS.OpenRead(AbiStampPath);
+  Copy(AbiStampPath, stamp);
+  h := M2makeOS.OpenRead(stamp);
   IF h = NIL THEN RETURN FALSE END;
   IF h = NIL THEN RETURN FALSE END;
   ok := M2makeOS.ReadLine(h, line, HIGH(line) + 1);
   ok := M2makeOS.ReadLine(h, line, HIGH(line) + 1);
   M2makeOS.CloseRead(h);
   M2makeOS.CloseRead(h);
@@ -944,7 +1027,7 @@ PROCEDURE BuildM2Separate(out: ARRAY OF CHAR);
 VAR cmd: Cmd; k, m: CARDINAL; rc: INTEGER;
 VAR cmd: Cmd; k, m: CARDINAL; rc: INTEGER;
     obj, src, ssafile, sfile, flags: Path;
     obj, src, ssafile, sfile, flags: Path;
     outT: LONGINT; compiledAny, linkNeeded, abiOK, hasGeneric: BOOLEAN;
     outT: LONGINT; compiledAny, linkNeeded, abiOK, hasGeneric: BOOLEAN;
-    gi, k2: INTEGER;
+    gi: INTEGER; k2: CARDINAL;
 BEGIN
 BEGIN
   Zero(cmd);
   Zero(cmd);
   abiOK := AbiStampOK();
   abiOK := AbiStampOK();
@@ -1067,8 +1150,11 @@ BEGIN
         INC(k)
         INC(k)
       END;
       END;
       IF SrcNewer(shimPath, outT) THEN linkNeeded := TRUE END;
       IF SrcNewer(shimPath, outT) THEN linkNeeded := TRUE END;
-      IF Len(extraC) > 0 THEN
-        IF SrcNewer(extraC, outT) THEN linkNeeded := TRUE END
+      k := 0;
+      WHILE k < nExtras DO
+        GetPool(extraPool, k, tmpP);
+        IF SrcNewer(tmpP, outT) THEN linkNeeded := TRUE END;
+        INC(k)
       END
       END
     END
     END
   END;
   END;
@@ -1093,7 +1179,9 @@ BEGIN
     INC(k)
     INC(k)
   END;
   END;
   AppendTok(tmpCmd, shimPath);
   AppendTok(tmpCmd, shimPath);
-  IF Len(extraC) > 0 THEN AppendTok(tmpCmd, extraC) END;
+  AppendExtras(tmpCmd);
+  AppendPkgConfigs(tmpCmd);
+  AppendLibs(tmpCmd);
   AppendSeg(tmpCmd, " -o ");
   AppendSeg(tmpCmd, " -o ");
   AppendTok(tmpCmd, out);
   AppendTok(tmpCmd, out);
   AppendSeg(tmpCmd, " -lm");
   AppendSeg(tmpCmd, " -lm");
@@ -1192,6 +1280,9 @@ BEGIN
       END;
       END;
       INC(k)
       INC(k)
     END;
     END;
+    AppendExtras(seg);
+    AppendPkgConfigs(seg);
+    AppendLibs(seg);
     AppendCmd(cmd, seg)
     AppendCmd(cmd, seg)
   END;
   END;
   IF Len(cmd) = 0 THEN
   IF Len(cmd) = 0 THEN
@@ -1257,6 +1348,9 @@ BEGIN
   Zero(m2Flags);
   Zero(m2Flags);
   dialect[0] := CHR(0);
   dialect[0] := CHR(0);
   nDirs := 0;
   nDirs := 0;
+  nExtras := 0;
+  nPkgs := 0;
+  nLibs := 0;
   na := 0;
   na := 0;
   LOOP
   LOOP
     IF na >= MaxArgs THEN Die("M2make: too many arguments", 2) END;
     IF na >= MaxArgs THEN Die("M2make: too many arguments", 2) END;
@@ -1343,7 +1437,29 @@ BEGIN
         M2makeOS.ExitNow(2)
         M2makeOS.ExitNow(2)
       END;
       END;
       INC(na);
       INC(na);
-      Copy(s, extraC)
+      IF nExtras >= MaxExtras THEN Die("M2make: too many --extra files", 2) END;
+      PutPool(extraPool, nExtras, s);
+      INC(nExtras)
+    ELSIF Cmp(s, "--pkg-config") = 0 THEN
+      ok := M2makeOS.GetArg(na, s, HIGH(s) + 1);
+      IF NOT ok THEN
+        Out("M2make: --pkg-config expects a package"); OutLn;
+        M2makeOS.ExitNow(2)
+      END;
+      INC(na);
+      IF nPkgs >= MaxExtras THEN Die("M2make: too many --pkg-config packages", 2) END;
+      PutPool(pkgPool, nPkgs, s);
+      INC(nPkgs)
+    ELSIF Cmp(s, "--lib") = 0 THEN
+      ok := M2makeOS.GetArg(na, s, HIGH(s) + 1);
+      IF NOT ok THEN
+        Out("M2make: --lib expects a library"); OutLn;
+        M2makeOS.ExitNow(2)
+      END;
+      INC(na);
+      IF nLibs >= MaxExtras THEN Die("M2make: too many --lib entries", 2) END;
+      PutPool(libPool, nLibs, s);
+      INC(nLibs)
     ELSIF (Cmp(s, "-h") = 0) OR (Cmp(s, "--help") = 0) THEN
     ELSIF (Cmp(s, "-h") = 0) OR (Cmp(s, "--help") = 0) THEN
       Usage;
       Usage;
       M2makeOS.ExitNow(0)
       M2makeOS.ExitNow(0)

+ 11 - 3
tools/m2make/README.md

@@ -9,7 +9,8 @@ to do), 1 build failure (incl. import cycles), 2 usage error.
 
 
 ```sh
 ```sh
 M2make [-n] [-v] [-c m2|gm2] [-I dir] [-o out]
 M2make [-n] [-v] [-c m2|gm2] [-I dir] [-o out]
-       [--m2bin path] [--shim path] [--extra path]
+       [--m2bin path] [--shim path] [--extra path]...
+       [--pkg-config pkg]... [--lib lib]...
        [--m2flag flag] [--dialect pim|iso|gm2] [--separate] main.mod
        [--m2flag flag] [--dialect pim|iso|gm2] [--separate] main.mod
 ```
 ```
 
 
@@ -23,7 +24,13 @@ M2make [-n] [-v] [-c m2|gm2] [-I dir] [-o out]
 - `--m2bin path`: M2 driver binary (default `M2`).
 - `--m2bin path`: M2 driver binary (default `M2`).
 - `--shim path`: C runtime shim for `m2` mode (default `shim.c`).
 - `--shim path`: C runtime shim for `m2` mode (default `shim.c`).
 - `--extra path`: extra C file linked in `m2` mode (after the shim;
 - `--extra path`: extra C file linked in `m2` mode (after the shim;
-  e.g. a tool's own `m2make_os.c`).
+  e.g. a tool's own `m2make_os.c`). Repeatable for multiple C shims.
+- `--pkg-config pkg`: append
+  `$(pkg-config --cflags --libs pkg)` to the link command.
+  Repeatable.
+- `--lib lib`: append one library to the link. A bare name `foo` means
+  `-lfoo`; a value beginning with `-` or `/` (for example `-L/dir` or
+  `/usr/lib/libfoo.so`) is appended unchanged. Repeatable.
 - `--m2flag flag`: pass one argument to the M2 driver before the source
 - `--m2flag flag`: pass one argument to the M2 driver before the source
   list (used by the bootstrap fixpoint for `-legacyopen1d`).
   list (used by the bootstrap fixpoint for `-legacyopen1d`).
 - `--dialect pim|iso|gm2`: add `lib/<dialect>`, `lib/common`,
 - `--dialect pim|iso|gm2`: add `lib/<dialect>`, `lib/common`,
@@ -37,7 +44,8 @@ M2make [-n] [-v] [-c m2|gm2] [-I dir] [-o out]
   compiler ABI/flags content.
   compiler ABI/flags content.
 
 
 `m2` mode (default, one-shot) runs one session — `<m2bin> <ordered .def/.mod files>`
 `m2` mode (default, one-shot) runs one session — `<m2bin> <ordered .def/.mod files>`
-then `qbe` then `cc <image>.s <shim> [<extra>] -o <out> -lm` — and
+then `qbe` then
+`cc <image>.s <shim> [<extra>...] [<pkg-config flags>] [<libs>] -o <out> -lm` — and
 gates on M2's `Parsed correctly` verdict (M2 exits 0 even on
 gates on M2's `Parsed correctly` verdict (M2 exits 0 even on
 failure).  Modules the session needs but that are not named on the
 failure).  Modules the session needs but that are not named on the
 command line are resolved from the `-I` directories (so
 command line are resolved from the `-I` directories (so

+ 15 - 0
tools/m2make/tests/LinkExtra.mod

@@ -0,0 +1,15 @@
+MODULE LinkExtra;
+(* Multiple extra C shims and a pkg-config/link test. *)
+
+PROCEDURE extra_one () : INTEGER; EXTERNAL "extra_one";
+PROCEDURE extra_two () : INTEGER; EXTERNAL "extra_two";
+
+VAR ExitCode : INTEGER;
+
+BEGIN
+  IF extra_one() + extra_two() = 42 THEN
+    ExitCode := 42
+  ELSE
+    ExitCode := 1
+  END
+END LinkExtra.

+ 1 - 0
tools/m2make/tests/extra_one.c

@@ -0,0 +1 @@
+long extra_one(void) { return 19; }

+ 1 - 0
tools/m2make/tests/extra_two.c

@@ -0,0 +1 @@
+long extra_two(void) { return 23; }

+ 10 - 0
tools/m2make/tests/m2make-test.pc

@@ -0,0 +1,10 @@
+prefix=/tmp
+exec_prefix=${prefix}
+libdir=${exec_prefix}/lib
+includedir=${prefix}/include
+
+Name: m2make-test
+Description: M2make link integration test
+Version: 1.0
+Libs: -lm
+Cflags: -I${includedir}

+ 32 - 1
tools/m2make/tests/run_tests.sh

@@ -89,6 +89,37 @@ case "$out" in
   *) no "m2-mode up-to-date skip: $out" ;;
   *) no "m2-mode up-to-date skip: $out" ;;
 esac
 esac
 
 
+# --- multiple extra C shims, --lib and --pkg-config (one-shot) ---
+rm -rf gen_ssa LinkExtra m2make.log
+OLD_PKG_CONFIG_PATH="$PKG_CONFIG_PATH"
+PKG_CONFIG_PATH="$PWD/tests${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}"
+export PKG_CONFIG_PATH
+if $V3M -c m2 --m2bin ../../compiler/M2 --shim ../../runtime/syslib/shim.c \
+     --extra tests/extra_one.c --extra tests/extra_two.c \
+     --pkg-config m2make-test --lib m -o LinkExtra tests/LinkExtra.mod \
+     > /tmp/m2make_t.log 2>&1; then
+  ./LinkExtra > /dev/null 2>&1; rc=$?
+  if [ "$rc" = "42" ]; then ok "multiple --extra + --pkg-config + --lib"
+  else no "multiple --extra + --pkg-config + --lib (rc=$rc)"; fi
+else
+  no "multiple --extra + --pkg-config + --lib (build)"
+fi
+
+# --- same link integration in separate-object mode ---
+rm -rf gen_ssa LinkExtraSep m2make.log
+if $V3M --separate -c m2 --m2bin ../../compiler/M2 --shim ../../runtime/syslib/shim.c \
+     --extra tests/extra_one.c --extra tests/extra_two.c \
+     --pkg-config m2make-test --lib m -o LinkExtraSep tests/LinkExtra.mod \
+     > /tmp/m2make_t.log 2>&1; then
+  ./LinkExtraSep > /dev/null 2>&1; rc=$?
+  if [ "$rc" = "42" ]; then ok "separate --extra + --pkg-config + --lib"
+  else no "separate --extra + --pkg-config + --lib (rc=$rc)"; fi
+else
+  no "separate --extra + --pkg-config + --lib (build)"
+fi
+PKG_CONFIG_PATH="$OLD_PKG_CONFIG_PATH"
+export PKG_CONFIG_PATH
+
 # --- separate-object build, up-to-date, incremental relink ---
 # --- separate-object build, up-to-date, incremental relink ---
 rm -rf gen_ssa SepMain m2make.log
 rm -rf gen_ssa SepMain m2make.log
 if $V3M --separate -c m2 --m2bin ../../compiler/M2 --shim ../../runtime/syslib/shim.c \
 if $V3M --separate -c m2 --m2bin ../../compiler/M2 --shim ../../runtime/syslib/shim.c \
@@ -183,7 +214,7 @@ else
   no "gm2-built M2make drives m2 build"
   no "gm2-built M2make drives m2 build"
 fi
 fi
 
 
-rm -f Main MainX Main2 SepMain CDefProg GenProg GCProg *.o
+rm -f Main MainX Main2 SepMain LinkExtra LinkExtraSep CDefProg GenProg GCProg *.o
 rm -rf gen_ssa
 rm -rf gen_ssa
 rm -f m2make.log
 rm -f m2make.log