Browse Source

Automatic linking of external libs on Linux

Arthur Yefimov 2 years ago
parent
commit
a1c1fec168
5 changed files with 160 additions and 104 deletions
  1. 15 14
      Data/bin/link.sh
  2. 0 55
      Data/bin/link_graph.sh
  3. 141 28
      src/Builder.Mod
  4. 2 3
      src/Fob.Mod
  5. 2 4
      src/FreeOberon.Mod

+ 15 - 14
Data/bin/link_console.sh → Data/bin/link.sh

@@ -1,6 +1,6 @@
 #!/bin/bash
 ### This script is run by Free Oberon on Linux
-### to link a console program.
+### to link user programs.
 ### When it is being run, the current directory
 ### must be the root directory of Free Oberon.
 
@@ -27,13 +27,18 @@ CC="gcc"
 
 shift
 
-
-
-
-
-
-
-
+FILES=
+FLAGS=
+F=false
+
+for arg in "$@"; do
+  if [ "$arg" = "--linker-libs" ]
+  then F=true
+  elif [ "$F" = false ]
+  then FILES="$FILES $arg"
+  else FLAGS="$FLAGS $arg"
+  fi
+done
 
 
 
@@ -42,13 +47,9 @@ $CC -O0 -fno-exceptions \
   -I $OFRDIR/Mod/Lib \
   -I $OFRTAR/Lib/Obj \
   $ONAME.c -o $ONAME \
-  $@ \
+  $FILES \
   $FOBDIR/Data/bin/libFreeOberon.a \
-  $OFRTAR/Lib/libOfront.a
-
-
-
-
+  $OFRTAR/Lib/libOfront.a $FLAGS
 
 retcode=$?
 cd ..

+ 0 - 55
Data/bin/link_graph.sh

@@ -1,55 +0,0 @@
-#!/bin/bash
-### This script is run by Free Oberon on Linux
-### to link a graphics program.
-### When it is being run, the current directory
-### must be the root directory of Free Oberon.
-
-# Set DIR = directory of this script
-SOURCE=${BASH_SOURCE[0]}
-while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
-  DIR=$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )
-  SOURCE=$(readlink "$SOURCE")
-  [[ $SOURCE != /* ]] && SOURCE=$DIR/$SOURCE # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
-done
-DIR=$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )
-
-cd _Build
-
-THENAME="${1%.*}"
-ONAME="${THENAME##*/}"
-
-FOBDIR="$DIR/../.."
-OFRDIR="$DIR/OfrontPlus"
-OFRTAR="$OFRDIR/Target/Linux_amd64"
-PATH="$OFRTAR:$PATH"
-CC="gcc"
-
-
-shift
-
-
-
-
-
-
-
-
-
-
-
-$CC -O0 -fno-exceptions \
-  -I $FOBDIR/src \
-  -I $OFRDIR/Mod/Lib \
-  -I $OFRTAR/Lib/Obj \
-  $ONAME.c -o $ONAME \
-  $@ \
-  $FOBDIR/Data/bin/libFreeOberon.a \
-  $OFRTAR/Lib/libOfront.a \
-  $(pkg-config \
-    allegro_primitives-5 allegro_image-5 allegro_audio-5 \
-    allegro_acodec-5 allegro_font-5 allegro_dialog-5 \
-    allegro-5 --libs --cflags)
-
-retcode=$?
-cd ..
-exit $retcode

+ 141 - 28
src/Builder.Mod

@@ -26,6 +26,7 @@ TYPE
     fname*: ARRAY 256 OF CHAR; (* Filename of module source *)
     libs*: ARRAY 4096 OF CHAR; (* String from speical comments *)
     foreign*: BOOLEAN; (* TRUE for bindings that do not produce a C-file *)
+    system*: BOOLEAN;
     next*: Module
   END;
 
@@ -34,6 +35,9 @@ TYPE
 
 VAR
   sysModules: Module;
+  target: ARRAY 64 OF CHAR; (* 'LINUX' or 'WIN32' *)
+  wrongLinkTarget: BOOLEAN; (* TRUE if target was given and does not match *)
+  findLinkInfo: Module; (* If not NIL, special comments will be saved here *)
   workDir: ARRAY 256 OF CHAR; (* Directory of main file of compiled program *)
 
 PROCEDURE IsSysModule(IN name: ARRAY OF CHAR): BOOLEAN;
@@ -42,6 +46,13 @@ BEGIN p := sysModules;
   WHILE (p # NIL) & (p.s # name) DO p := p.next END ;
 RETURN p # NIL END IsSysModule;
 
+PROCEDURE GetSysLibs(IN name: ARRAY OF CHAR; OUT libs: ARRAY OF CHAR);
+VAR p: Module;
+BEGIN p := sysModules;
+  WHILE (p # NIL) & (p.s # name) DO p := p.next END ;
+  IF p # NIL THEN libs := p.libs$ ELSE libs[0] := 0X END
+END GetSysLibs;
+
 PROCEDURE ModuleExists(IN fname: ARRAY OF CHAR): BOOLEAN;
 VAR F: Files.File;
   exists: BOOLEAN;
@@ -177,7 +188,7 @@ BEGIN
   END
 END AppendDataBin;
 
-PROCEDURE RunCommand(IN fname, mod: ARRAY OF CHAR; link, graph, main: BOOLEAN;
+PROCEDURE RunCommand(IN fname, mod: ARRAY OF CHAR; link, main: BOOLEAN;
     list: Module; onError: ErrorHandler): BOOLEAN;
 CONST bufLen = 20480;
 VAR buf: ARRAY bufLen OF SHORTCHAR;
@@ -190,10 +201,7 @@ VAR buf: ARRAY bufLen OF SHORTCHAR;
   s, sN: ARRAY 80 OF CHAR;
   success, ok: BOOLEAN;
 BEGIN ok := TRUE;
-  IF ~link THEN command := 'compile'
-  ELSIF graph THEN command := 'link_graph'
-  ELSE command := 'link_console'
-  END;
+  IF ~link THEN command := 'compile' ELSE command := 'link' END;
   IF Config.isWindows THEN
     IF Term.SearchPath('cmd.exe', q) # 0 THEN
       Utf8.Decode(q, cmd); Strings.Insert('"', 0, cmd);
@@ -220,13 +228,20 @@ BEGIN ok := TRUE;
           Strings.Append('.c', cmd)
         END;
         p := p.next
+      END;
+      Strings.Append(' --linker-libs', cmd);
+      p := list;
+      WHILE p.next # NIL DO
+        IF p.libs[0] # 0X THEN
+          Strings.Append(' ', cmd); Strings.Append(p.libs, cmd)
+        END;
+        p := p.next
       END
     END;
-    Utf8.Encode(cmd, q);
-
     IF Config.debug THEN
-      Out.String('Term.RunProcess "'); Out.String(cmd); Out.Char('"'); Out.Ln
+      Out.String('Running command "');Out.String(cmd); Out.Char('"'); Out.Ln
     END;
+    Utf8.Encode(cmd, q);
 
     success := (Term.RunProcess(q, buf, bufLen, len, err) # 0) & (err = 0);
     IF ~success & (onError # NIL) THEN
@@ -250,16 +265,16 @@ RETURN success END RunCommand;
 
 PROCEDURE Compile(IN fname, mod: ARRAY OF CHAR; main: BOOLEAN;
   onError: ErrorHandler): BOOLEAN;
-BEGIN RETURN RunCommand(fname, mod, FALSE, FALSE, main, NIL, onError)
+BEGIN RETURN RunCommand(fname, mod, FALSE, main, NIL, onError)
 END Compile;
 
 PROCEDURE Link(IN fname, mod: ARRAY OF CHAR;
-    graph: BOOLEAN; list: Module; VAR exename: ARRAY OF CHAR;
+    list: Module; VAR exename: ARRAY OF CHAR;
     onError: ErrorHandler; moveToCwd: BOOLEAN): BOOLEAN;
 VAR ok: BOOLEAN;
   s: ARRAY 2048 OF CHAR;
   res: INTEGER;
-BEGIN ok := RunCommand(fname, mod, TRUE, graph, FALSE, list, onError);
+BEGIN ok := RunCommand(fname, mod, TRUE, FALSE, list, onError);
   IF ok THEN (* Move executable file if workDir is non-standard *)
     s := mod$; IF Config.isWindows THEN Strings.Append('.exe', s) END;
     exename := '_Build/'; Strings.Append(s, exename);
@@ -282,20 +297,113 @@ PROCEDURE ResetSysModules*;
     p.next := sysModules; sysModules := p
   END Add;
 
+  PROCEDURE AddGraph;
+  VAR p: Module;
+  BEGIN
+    Add('Graph'); p := sysModules;
+    IF Config.isWindows THEN
+      p.libs := '-lallegro -lallegro_primitives -lallegro_image';
+      Strings.Append(' -Wl,-subsystem,window', p.libs)
+    ELSE
+      p.libs := '$(pkg-config allegro_primitives-5 allegro_image-5';
+      Strings.Append(' allegro_audio-5 allegro_acodec-5', p.libs);
+      Strings.Append(' allegro_font-5 allegro_dialog-5 allegro-5', p.libs);
+      Strings.Append(' --libs --cflags)', p.libs)
+    END
+  END AddGraph;
+
 BEGIN sysModules := NIL;
   Add('SYSTEM');   Add('Texts');    Add('Files');   Add('Strings');
   Add('In');       Add('Out');      Add('Math');    Add('MathL');
   Add('Modules');  Add('Platform'); Add('Oberon');  Add('Reals');
-  Add('VT100');    Add('Graph');    Add('TermBox'); Add('Term');
+  Add('VT100');    AddGraph;        Add('TermBox'); Add('Term');
   Add('Allegro5'); Add('Dir');      Add('Int');     Add('Random')
 END ResetSysModules;
 
-PROCEDURE SkipComment(VAR R: Files.Rider; VAR ch: CHAR; VAR s: ARRAY OF CHAR);
+PROCEDURE GetWord(IN s: ARRAY OF CHAR; VAR i: INTEGER; OUT w: ARRAY OF CHAR);
+VAR j: INTEGER;
+  c: CHAR;
+BEGIN j := 0;
+  WHILE (s[i] <= ' ') & (s[i] # 0X) DO INC(i) END;
+  c := s[i]; INC(i);
+  IF ('A' <= c) & (c <= 'Z') OR ('a' <= c) & (c <= 'z') THEN
+    WHILE ('A' <= c) & (c <= 'Z') OR ('a' <= c) & (c <= 'z') OR
+          ('0' <= c) & (c <= '9') OR (c = '_')
+    DO
+      IF ('a' <= c) & (c <= 'z') THEN
+        c := CHR(ORD('A') - ORD('a') + ORD(c))
+      END;
+      w[j] := c;
+      INC(j); c := s[i]; INC(i)
+    END;
+    DEC(i)
+  ELSE w[j] := c; INC(j)
+  END;
+  w[j] := 0X
+END GetWord;
+
+PROCEDURE ReadLinkTarget(IN s: ARRAY OF CHAR; VAR i: INTEGER);
+VAR w: ARRAY 256 OF CHAR;
+BEGIN
+  wrongLinkTarget := TRUE;
+  GetWord(s, i, w);
+  IF w = 'LIBS' THEN
+    wrongLinkTarget := FALSE;
+    GetWord(s, i, w);
+    IF w = '(' THEN
+      GetWord(s, i, w);
+      IF w # target THEN wrongLinkTarget := TRUE END;
+      GetWord(s, i, w);
+      IF w = ')' THEN GetWord(s, i, w) ELSE wrongLinkTarget := TRUE END
+    END;
+    IF w # ':' THEN wrongLinkTarget := TRUE END
+  END
+END ReadLinkTarget;
+
+PROCEDURE ReadLinkString(IN s: ARRAY OF CHAR; VAR i: INTEGER);
+VAR j: INTEGER;
+  c: CHAR;
+BEGIN
+  IF ~wrongLinkTarget THEN j := 0;
+    WHILE (s[i] # 0X) & (s[i] <= ' ') DO INC(i) END;
+    WHILE (s[i] # 0X) & (j < LEN(findLinkInfo.libs) - 1) DO
+      IF s[i] < ' ' THEN c := ' ' ELSE c := s[i] END;
+      findLinkInfo.libs[j] := c;
+      INC(i); INC(j);
+      IF c <= ' ' THEN
+        WHILE (s[i] # 0X) & (s[i] <= ' ') DO INC(i) END
+      END
+    END;
+    WHILE (j # 0) & (findLinkInfo.libs[j] <= ' ') DO DEC(j) END;
+    findLinkInfo.libs[j + 1] := 0X
+  END
+END ReadLinkString;
+
+PROCEDURE ReadLinkInfo(VAR R: Files.Rider; VAR ch: CHAR);
+VAR s: ARRAY 40960 OF CHAR;
+  last: CHAR;
+  i: INTEGER;
+BEGIN
+  i := 0; last := ch;
+  WHILE ~R.eof & (ch # '%') & ~((last = '*') & (ch = ')')) DO
+    IF i < LEN(s) - 1 THEN s[i] := ch; INC(i) END;
+    last := ch; Files.ReadChar(R, ch)
+  END;
+  IF (i # 0) & (s[i - 1] = '*') THEN DEC(i) END;
+  s[i] := 0X; i := 0;
+  ReadLinkTarget(s, i);
+  ReadLinkString(s, i)
+END ReadLinkInfo;
+
+PROCEDURE SkipComment(VAR R: Files.Rider; VAR ch: CHAR);
 VAR last: CHAR;
 BEGIN last := ch; Files.ReadChar(R, ch);
-  WHILE ~R.eof & ((last # '*') OR (ch # ')')) DO
-    IF (last = '(') & (ch = '*') THEN SkipComment(R, ch, s) END;
-    last := ch; Files.ReadChar(R, ch)
+  WHILE ~R.eof & ~((last = '*') & (ch = ')')) DO
+    IF (last = '(') & (ch = '*') THEN SkipComment(R, ch)
+    ELSIF (ch = '%') & (findLinkInfo # NIL) THEN
+      Files.ReadChar(R, ch); ReadLinkInfo(R, ch)
+    ELSE last := ch; Files.ReadChar(R, ch)
+    END
   END;
   IF ~R.eof THEN Files.ReadChar(R, ch) END;
   WHILE ~R.eof & (ch <= ' ') DO Files.ReadChar(R, ch) END
@@ -315,7 +423,7 @@ BEGIN
   IF ~R.eof THEN
     WHILE ch = '(' DO
       ReadCh(R, ch, line, col);
-      IF ch = '*' THEN ReadCh(R, ch, line, col); SkipComment(R, ch, s)
+      IF ch = '*' THEN ReadCh(R, ch, line, col); SkipComment(R, ch)
       ELSE s[i] := ch; INC(i)
       END
     END;
@@ -338,7 +446,7 @@ BEGIN
   s[i] := 0X
 END GetSym;
 
-PROCEDURE CompileAll*(modules: Module; graph: BOOLEAN;
+PROCEDURE CompileAll*(modules: Module;
     VAR exename: ARRAY OF CHAR; moveToCwd: BOOLEAN;
     onError: ErrorHandler): BOOLEAN;
 VAR p, last: Module;
@@ -357,8 +465,7 @@ BEGIN exename[0] := 0X;
       IF ~ModuleExists(p.fname) OR ~Compile(p.fname, '', TRUE, onError) THEN
         ok := FALSE
       END;
-      ok := ok & Link(p.fname, p.s, graph, modules,
-          exename, onError, moveToCwd);
+      ok := ok & Link(p.fname, p.s, modules, exename, onError, moveToCwd)
     END
   ELSE ok := FALSE
   END ;
@@ -395,9 +502,9 @@ BEGIN mod.foreign := FALSE; res := 401; NEW(top); top.next := NIL; p := top;
         IF ~R.eof THEN GetSym(R, ch, s, line, col) END
       END;
       IF EqualModuleNames(mod.s, s) THEN
-        res := 0;
-        GetSym(R, ch, s, line, col);
-        IF s = ';' THEN GetSym(R, ch, s, line, col); res := 0;
+        res := 0; findLinkInfo := mod; GetSym(R, ch, s, line, col);
+        IF s = ';' THEN
+          GetSym(R, ch, s, line, col); res := 0; findLinkInfo := NIL;
           IF s = 'IMPORT' THEN GetSym(R, ch, s, line, col); exit := FALSE;
             WHILE ~exit & ('A' <= CAP(s[0])) & (CAP(s[0]) <= 'Z') DO
               m := s; GetSym(R, ch, s, line, col); fname2[0] := 0X;
@@ -405,15 +512,16 @@ BEGIN mod.foreign := FALSE; res := 401; NEW(top); top.next := NIL; p := top;
                 m := s; GetSym(R, ch, s, line, col)
               END;
               IF IsSysModule(m) OR FindModule(m, fname2) THEN
-                NEW(p.next); p := p.next; p.next := NIL;
-                p.s := m$; p.fname := fname2$; p.foreign := FALSE
+                NEW(p.next); p := p.next; p.next := NIL; p.s := m$;
+                p.fname := fname2$; p.foreign := FALSE; GetSysLibs(m, p.libs)
               END;
               IF s = ',' THEN GetSym(R, ch, s, line, col)
               ELSE exit := FALSE
               END
             END
           END
-        END
+        END;
+        findLinkInfo := NIL
       END
     END
   END ;
@@ -448,7 +556,11 @@ VAR L, list, list2, p, mod: Module;
 BEGIN L := NIL; res := 0(*OK*);
   NEW(mod); mod.s := modname$; mod.fname := fname$;
   mod.foreign := FALSE; mod.next := NIL;
-  IF ~IsSysModule(modname) THEN
+  IF IsSysModule(modname) THEN
+    NEW(p); p.next := NIL; p.s := modname$; p.fname[0] := 0X;
+    p.foreign := FALSE; p.system := TRUE; GetSysLibs(modname, p.libs);
+    AddUniqueToList(p, L)
+  ELSE
     list := GetModuleInfo(mod, errLine, errCol, res);
     p := list;
     IF res = 0 THEN
@@ -467,5 +579,6 @@ BEGIN L := NIL; res := 0(*OK*);
 RETURN L END UsedModuleList;
 
 BEGIN
-  ResetSysModules
+  ResetSysModules;
+  IF Config.isWindows THEN target := 'WIN32' ELSE target := 'LINUX' END
 END Builder.

+ 2 - 3
src/Fob.Mod

@@ -71,7 +71,7 @@ PROCEDURE Do;
 VAR modules: Builder.Module;
   mainFname, modname, exename, errFname, lang, s: ARRAY 256 OF CHAR;
   errLine, errCol, res: INTEGER;
-  foreign, ok, graph: BOOLEAN;
+  foreign, ok: BOOLEAN;
 BEGIN
   ParseArgs(mainFname, lang);
   FoStrings.SetLang(lang);
@@ -82,8 +82,7 @@ BEGIN
   IF foreign THEN res := 402 END;
   ok := FALSE;
   IF res = 0 THEN
-    graph := Builder.Includes(modules, 'Graph');
-    IF Builder.CompileAll(modules, graph, exename, TRUE, BuildErrorCallback)
+    IF Builder.CompileAll(modules, exename, TRUE, BuildErrorCallback)
     THEN ok := TRUE
     END
   ELSE (*res = 400-bad file, 401-bad module name, or 402-foreign module*)

+ 2 - 4
src/FreeOberon.Mod

@@ -507,7 +507,7 @@ END BuildErrorCallback;
 
 PROCEDURE OnBuild(c: OV.Control);
 VAR w: OV.Window;
-  graph, foreign: BOOLEAN;
+  foreign: BOOLEAN;
   mainFname, modname, exename, errFname, s: ARRAY 256 OF CHAR;
   errLine, errCol, res: INTEGER;
   modules: Builder.Module;
@@ -523,9 +523,7 @@ BEGIN w := c.app.windows;
         errFname, errLine, errCol, foreign, res);
       IF foreign THEN res := 402 END;
       IF res = 0 THEN
-        graph := Builder.Includes(modules, 'Graph');
-        IF Builder.CompileAll(modules, graph, exename,
-            FALSE, BuildErrorCallback)
+        IF Builder.CompileAll(modules, exename, FALSE, BuildErrorCallback)
         THEN RunProgram(exename)
         END
       ELSE