MODULE M3DepM3Makefile; (* Copyright (C) 1991, Digital Equipment Corporation *) (* All rights reserved. *) (* See the file COPYRIGHT for a full description. *) IMPORT CharType, PathNameStream, Fmt, HashText, IO, IOErr, Err, FileOp, M3Extension, M3Assert, M3Env, M3AST_AS, M3Args, M3CId, M3CUnit, M3CUnit_priv, M3Context, M3Conventions, M3CLiteral, M3DepM3MFTool, M3DepM3Path, M3LMain, M3Path, PathName, StdIO, Text, TextExtras, WrapStream, SList; IMPORT M3AST_AS_F, M3AST_SM_F, M3AST_FE_F, M3AST_PL_F, M3AST_PG_F; IMPORT SeqM3AST_AS_Used_interface_id, SeqM3AST_AS_Module; TYPE MacroName = {CPP, M3CPPFLAGS, M3C, M3L, M3CFLAGS, M3LFLAGS, M3LOBJS, M3LLIBS, AR, ARFLAGS, M3AR, CC, CFLAGS, IDIRS, RANLIB}; CONST MacroText = ARRAY MacroName OF TEXT {"CPP", "M3CPPFLAGS", "M3C", "M3L", "M3CFLAGS", "M3LFLAGS", "M3LOBJS", "M3LLIBS", "AR", "ARFLAGS", "M3AR", "CC", "CFLAGS", "IDIRS", "RANLIB"}; MacroDefault = ARRAY MacroName OF TEXT {"/lib/cpp", "", "m3", "$(M3C)", "", "", "", "", "ar", "crul", "m3ar", "cc", "", ".", "ranlib"}; LIBDIRFLAG = 1024; (* used to indicate an archive file in a directory *) TYPE DirClosure = M3Context.Closure OBJECT dir: TEXT; END; VAR stream_g: IO.Stream; (* output stream (wrapping) *) dirTable_g: HashText.Table; (* Mapping of directories to DIR macros *) macros_g: HashText.Table := HashText.New(15); (* macro values *) (*PUBLIC*) PROCEDURE Run(c: M3Context.T; p: M3DepM3Path.T): INTEGER RAISES {}= BEGIN IF NOT M3Args.Find(M3DepM3MFTool.Get()) THEN RETURN -1; END; (*CheckDefinedMacros();*) VAR fileName := M3Args.GetString(M3DepM3MFTool.Get(), M3DepM3MFTool.MakeFile_Arg); backingStream: IO.Stream; wrapStream: IO.Stream; BEGIN TRY (* except IO.Error *) IF fileName = NIL THEN fileName := M3DepM3MFTool.MakeFileDefault; END; IF Text.Equal(fileName, M3DepM3MFTool.StdOut_Arg) THEN backingStream := StdIO.Out(); ELSE backingStream := PathNameStream.Open(fileName, IO.OpenMode.Write); END; (* open makefile *) wrapStream := WrapStream.Open( backingStream := backingStream, margin := 80, breakChars := " \t", eol := "\\", bol := " ", openMode := IO.OpenMode.Write, closeBackingStream := NOT Text.Equal(fileName, M3DepM3MFTool.StdOut_Arg)); DoMakeFile(c, p, fileName, wrapStream, ""); IO.Close(wrapStream); EXCEPT IO.Error(s) => IOErr.Close(s, Err.Severity.Error); RETURN -1 END; (* try *) END; (* var *) RETURN 0; END Run; (*PRIVATE*) PROCEDURE DoMakeFile(c: M3Context.T; p: M3DepM3Path.T; fileName: TEXT; s: IO.Stream; dir: TEXT) RAISES {}= VAR mainMods := SList.T{}; BEGIN stream_g := s; (* calculate program modules (if any) *) mainMods := ThisDirOnly(M3LMain.Module(c, NIL), dir); (* start printing things out *) (*MakefilePreamble();*) DIRMacros(p); (* sources *) FilesMacro(c, M3Extension.T.Int, dir); FilesMacro(c, M3Extension.T.Mod, dir); (*ExternalMacros(c, dir);*) DoLibraries(p, FALSE); PROGSMacro(c, mainMods); Put("M3DEFPATH = -D$(DDIRS)\n\n"); DoLibraries(p, TRUE); MakefileCoda(CheckErrors(c)); END DoMakeFile; PROCEDURE CheckErrors(c: M3Context.T): BOOLEAN RAISES {}= VAR name: TEXT; cu: M3AST_AS.Compilation_Unit; iter: M3Context.Iter; BEGIN FOR ut := FIRST(M3CUnit.Type) TO LAST(M3CUnit.Type) DO iter := M3Context.NewIter(c, ut); WHILE M3Context.Next(iter, name, cu) DO IF cu.fe_status * M3CUnit.Errors # M3CUnit.Status{} THEN RETURN TRUE END; (* if *) END; (* while *) END; RETURN FALSE; END CheckErrors; (*PRIVATE*) PROCEDURE ThisDirOnly(list: SList.T; dir: TEXT): SList.T RAISES {} = (* filter SList of cu's for those in 'dir'. *) VAR ret := SList.T{}; elem: M3LMain.CuElem; file: TEXT; BEGIN elem := list.head; WHILE elem # NIL DO IF Text.Equal(M3DepM3Path.RealHead(M3CUnit.TextName(elem.cu.fe_uid)), dir) THEN SList.AddFront(ret, NEW(M3LMain.CuElem, cu := elem.cu)); END; elem := elem.next; END; RETURN ret; END ThisDirOnly; (*PRIVATE*) PROCEDURE MakefilePreamble() RAISES {}= BEGIN Put(Fmt.F( "/* m3makefile generated by m3depmf version %s */\n\n", M3DepM3MFTool.Version)); FOR m := FIRST(MacroName) TO LAST(MacroName) DO Put(Fmt.F("%s = %s\n", MacroText[m], GetMacro(m))); END; (* for *) END MakefilePreamble; (*PRIVATE*) PROCEDURE DIRMacros(m3Path: M3DepM3Path.T) RAISES {}= VAR i: INTEGER := 0; refI: REF INTEGER; hid: HashText.Id; name: M3Path.Elem; problem: INTEGER; localDir := -1; ddirs := "."; ldirs := "."; BEGIN Put("/* Directories (except .) on the search path */\n"); dirTable_g := HashText.New(40); name := M3DepM3Path.Dirs(m3Path).head; WHILE name # NIL DO IF NOT IsLocalDir(name.text) THEN EVAL HashText.Enter(dirTable_g, name.text, hid); refI := NEW(REF INTEGER); refI^ := i; Put(Fmt.F("DIR%s = %s\n", Fmt.Int(i), name.unexpanded)); IF M3DepM3Path.DirObjLib(m3Path, name) # NIL THEN INC(refI^, LIBDIRFLAG); ldirs := ldirs & Fmt.F(":$(DIR%s)", Fmt.Int(i)); END; HashText.Associate(dirTable_g, hid, refI); IF NOT StandardRepository(name.text) AND ContainsSources(m3Path, name) THEN (* Add to search path *) ddirs := ddirs & Fmt.F(":$(DIR%s)", Fmt.Int(i)); END; INC(i); ELSE localDir := i; END; (*if not current dir*) name := name.next; END; (*while m3path*) Put("\n"); EVAL WrapStream.Set(stream_g, FALSE); Put("DDIRS = " & ddirs); Put("\n"); Put("LDIRS = " & ldirs); Put("\n\n"); EVAL WrapStream.Set(stream_g, TRUE); END DIRMacros; PROCEDURE StandardRepository(name: TEXT): BOOLEAN RAISES {}= BEGIN RETURN Text.Equal(name, M3Env.Std_Interface_Rep()) OR Text.Equal(name, M3Env.Std_Library_Rep()); END StandardRepository; PROCEDURE ContainsSources(p: M3DepM3Path.T; dirElem: M3Path.Elem): BOOLEAN= VAR ints, mods: M3DepM3Path.UpdateRec; BEGIN M3DepM3Path.Interfaces(NIL, p, ints, dirElem); M3DepM3Path.Modules(NIL, p, mods, dirElem); RETURN ints[M3DepM3Path.Update.Added].head # NIL OR mods[M3DepM3Path.Update.Added].head # NIL END ContainsSources; TYPE FilesMacroClosure = M3Context.Closure OBJECT ext: M3Extension.T; dir: TEXT := "!@!"; END; CONST ModuleExts = M3Extension.TSet{ M3Extension.T.Mod, M3Extension.T.PMod, M3Extension.T.ModPp, M3Extension.T.PModR, M3Extension.T.MObj, M3Extension.T.MC, M3Extension.T.MAsm}; InterfaceExts = M3Extension.TSet{ M3Extension.T.Int, M3Extension.T.PInt, M3Extension.T.IntPp, M3Extension.T.PIntR, M3Extension.T.IObj, M3Extension.T.IC, M3Extension.T.IAsm}; (*PRIVATE*) PROCEDURE FilesMacro( c: M3Context.T; ext: M3Extension.T; dir: TEXT; ) RAISES {} = BEGIN M3Context.Apply( c, NEW(FilesMacroClosure, callback := GenFile, ext := ext, dir := dir), findStandard := FALSE); Put("\n"); END FilesMacro; (*PRIVATE*) PROCEDURE GenFile( cl: FilesMacroClosure; ut: M3CUnit.Type; name: Text.T; cu: M3AST_AS.Compilation_Unit) RAISES {}= VAR file, call: TEXT; void: INTEGER; BEGIN file := M3CUnit.TextName(cu.fe_uid); IF Text.Equal(M3DepM3Path.RealHead(file), cl.dir) AND ((ut IN M3CUnit.Interfaces AND cl.ext IN InterfaceExts) OR (ut IN M3CUnit.Modules AND cl.ext IN ModuleExts)) THEN IF ut = M3CUnit.Type.Interface_gen_def THEN call := "Generic_interface" ELSIF ut IN M3CUnit.Interfaces THEN call := "Interface" ELSIF ut = M3CUnit.Type.Module_gen_def THEN call := "generic_implementation"; ELSE call := "implementation" END; Put(Fmt.F("%s(%s)\n", call, PathName.Name(file))); END; (* if ut & extension match *) END GenFile; PROCEDURE ExternalMacros(c: M3Context.T; dir: TEXT) RAISES {}= VAR iter := M3Context.NewIter(c, M3CUnit.Type.Interface); name, cname, uname: TEXT; cu: M3AST_AS.Compilation_Unit; language := "C"; list := SList.T{}; BEGIN WHILE M3Context.Next(iter, name, cu) DO IF cu.fe_status * M3CUnit.Errors = M3CUnit.Status{} THEN VAR pg_external := NARROW(cu.as_root, M3AST_AS.Interface).vEXTERNAL_DECL.pg_external; index: CARDINAL := 0; BEGIN (* EXTERNAL and in 'dir' *) IF pg_external # NIL AND Text.Equal(M3DepM3Path.RealHead(M3CUnit.TextName(cu.fe_uid)), dir) THEN (* default is to set 'uname := name' *) uname := name; IF pg_external.lx_lang_spec # NIL THEN name := M3CLiteral.ToText(pg_external.lx_lang_spec); (* strip quotes (if any) *) IF Text.GetChar(name, 0) = '\"' THEN name := Text.Sub(name, 1, Text.Length(name)-2); END; IF TextExtras.FindChar(name, ':', index) THEN IF index # 0 THEN uname := TextExtras.Extract(name, 0, index); END; (* language denotation is Extract(name, index, Length(name)) *) ELSE uname := name; END; (* if *) END; (* assume its C for now, look for uname.c *) cname := PathName.Concat(dir, PathName.Extend(uname, "c")); IF FileOp.GetInfo(cname, mustExist := FALSE) # NIL THEN (* YO! got one *) SList.AddFront(list, NEW(SList.TextElem, text := cname)); END; (* if *) END; (* if *) END; (* begin *) END; (* if *) END; (* while *) VAR void: INTEGER; elem: SList.TextElem := list.head; BEGIN Put(Fmt.F("%s =", ExternalMac(TRUE))); WHILE elem # NIL DO Put(Fmt.F(" %s", MakeName(elem.text, void))); elem := elem.next; END; (* while *) Put("\n\n"); Put(Fmt.F("%s =", ExternalMac(FALSE))); elem := list.head; WHILE elem # NIL DO Put(Fmt.F(" %s", MakeName(PathName.Extend(elem.text, "o"), void))); elem := elem.next; END; (* while *) Put("\n\n"); END; END ExternalMacros; PROCEDURE ExternalMac(source: BOOLEAN): TEXT RAISES {}= BEGIN IF source THEN RETURN "EXTSRCS" ELSE RETURN "EXTOBJS" END; END ExternalMac; (*PRIVATE*) PROCEDURE PROGSMacro( c: M3Context.T; mainMods: SList.T) RAISES {}= VAR mod: M3LMain.CuElem := mainMods.head; BEGIN IF mod = NIL THEN WITH ar = M3Args.GetString(M3DepM3MFTool.Get(), M3DepM3MFTool.AR_Arg) DO IF ar # NIL THEN Put(Fmt.F("Library(%s)\n", ar)); END; END; ELSE WHILE mod # NIL DO Put(Fmt.F("Program(%s)\n", UnExtName(mod.cu))); mod := mod.next; END; END; Put("\n"); END PROGSMacro; PROCEDURE DoLibraries(p: M3DepM3Path.T; forLibPath: BOOLEAN) RAISES {}= VAR dir: M3Path.Elem := M3DepM3Path.Dirs(p).head; BEGIN IF forLibPath THEN Put("M3LIBPATH = -L$(LDIRS)") ELSE WHILE dir # NIL DO VAR dl := M3DepM3Path.DirObjLib(p, dir); void: INTEGER; BEGIN IF dl # NIL THEN IF NOT forLibPath THEN (* take the "lib" off the library name *) VAR ln := PathName.Name(dl); BEGIN Put(Fmt.F("import_lib(%s)\n", Text.Sub(ln, 3, Text.Length(ln)-3))); END; ELSE Put(MakeName(dir.unexpanded, void)); IF dir.next # NIL THEN Put(":"); END; END; END; END; dir := dir.next; END; END; Put("\n"); END DoLibraries; (*PRIVATE*) PROCEDURE MakefileCoda(errors: BOOLEAN) RAISES {}= BEGIN IF errors THEN VAR m := "errors generated - Makefile may be incomplete"; BEGIN Err.Print(m, Err.Severity.Warning); Put(Fmt.F("\n/* %s */\n", m)); END; END; (* if warning *) END MakefileCoda; (*PRIVATE*) PROCEDURE IsMakeTarget(cu: M3AST_AS.Compilation_Unit; dir: TEXT ): BOOLEAN RAISES {}= BEGIN RETURN Text.Equal(M3DepM3Path.RealHead(M3CUnit.TextName(cu.fe_uid)), dir); END IsMakeTarget; (* variations on the file name of a Compilation_Unit. *) (*PRIVATE*) PROCEDURE UnExtName(cu: M3AST_AS.Compilation_Unit): Text.T RAISES {}= (* Return the file name w/o extension for a Compilation_Unit. * i.e., for "/foo/bar/clam-one.m", return "clam-one". *) BEGIN RETURN PathName.Name(M3CUnit.TextName(cu.fe_uid)); END UnExtName; (*PRIVATE*) PROCEDURE ExtMac(ext: M3Extension.T; targetsOnly := TRUE): Text.T RAISES {} = BEGIN IF NOT targetsOnly AND ext IN InterfaceExts THEN RETURN Fmt.F("ALL%sS", ExtText(ext)); ELSE RETURN Fmt.F("%sS", ExtText(ext)); END; END ExtMac; (*PRIVATE*) PROCEDURE ExtText(ext: M3Extension.T): Text.T RAISES {} = BEGIN CASE ext OF | M3Extension.T.MObj => RETURN "MODOBJ"; | M3Extension.T.IObj => RETURN "INTOBJ"; | M3Extension.T.Int => RETURN "INTSRC"; | M3Extension.T.Mod => RETURN "MODSRC"; ELSE RETURN ToUpper(M3Extension.ToText(ext)); END; END ExtText; (*PRIVATE*) PROCEDURE ToUpper(t: Text.T): Text.T RAISES {} = VAR cap := NEW(REF ARRAY OF CHAR, Text.Length(t)); BEGIN Text.SetChars(cap^, t); FOR i := 0 TO LAST(cap^) DO cap[i] := CharType.ToUpper(cap[i]); END; (* for each char *) RETURN Text.FromChars(cap^); END ToUpper; (*PRIVATE*) PROCEDURE ExtName( cu: M3AST_AS.Compilation_Unit; ext: M3Extension.T) : Text.T RAISES {} = VAR void: INTEGER; BEGIN RETURN MakeName(PathName.Extend( M3CUnit.TextName(cu.fe_uid), M3Extension.ToText(ext)), void); END ExtName; (*PRIVATE*) PROCEDURE XExtName( cu: M3AST_AS.Compilation_Unit; ext: M3Extension.T; VAR (*out*) dirX: INTEGER) : Text.T RAISES {} = BEGIN RETURN MakeName(PathName.Extend( M3CUnit.TextName(cu.fe_uid), M3Extension.ToText(ext)), dirX); END XExtName; (*PRIVATE*) PROCEDURE MakeName(name: Text.T; VAR (*out*) dirX: INTEGER): Text.T RAISES{}= (* Return the makefile name - with the directory replaced by $(DIR#) *) VAR directory: Text.T := M3DepM3Path.RealHead(name); hid: HashText.Id; makeName: Text.T := ""; BEGIN dirX := -1; IF NOT IsLocalDir(directory) THEN IF HashText.Lookup(dirTable_g, directory, hid) THEN dirX := NARROW(HashText.Value(dirTable_g, hid), REF INTEGER)^; makeName := Fmt.F( "$(DIR%s)%s", Fmt.Int(dirX MOD LIBDIRFLAG), Fmt.Char(PathName.DirSepCh())); ELSE makeName := directory & Fmt.Char(PathName.DirSepCh()); END; (* if in hash table *) END; (* if not local directory *) (* name part *) makeName := makeName & PathName.Tail(name); RETURN makeName; END MakeName; (*PRIVATE*) PROCEDURE IsLocalDir(s: Text.T): BOOLEAN RAISES{}= BEGIN RETURN Text.Equal(s, PathName.Current()) OR Text.Equal(s, ""); END IsLocalDir; (*PRIVATE*) PROCEDURE Put(t: Text.T) RAISES{}= BEGIN IO.PutText(stream_g, t); END Put; (*PRIVATE*) PROCEDURE EnterMacro(t: TEXT; v: TEXT; init := FALSE) RAISES {}= VAR id: HashText.Id; BEGIN IF HashText.Enter(macros_g, t, id) THEN IF NOT init THEN Err.Print(Fmt.F("unknown macro \'%s\'", t), Err.Severity.Warning); RETURN END; (* if *) END; HashText.Associate(macros_g, id, v); END EnterMacro; (*PRIVATE*) PROCEDURE GetMacro(n: MacroName): TEXT RAISES {}= VAR id: HashText.Id; BEGIN M3Assert.Check(HashText.Lookup(macros_g, MacroText[n], id)); RETURN NARROW(HashText.Value(macros_g, id), TEXT); END GetMacro; (*PRIVATE*) PROCEDURE CheckDefinedMacros() RAISES {}= VAR t := M3Args.GetStringList(M3DepM3MFTool.Get(), M3DepM3MFTool.DefineMacros_Arg); macro: TEXT; index: CARDINAL := 0; BEGIN IF t = NIL THEN RETURN END; FOR i := 0 TO NUMBER(t^)-1 DO macro := t[i]; IF TextExtras.FindChar(macro, '=', index) THEN EnterMacro(TextExtras.Extract(macro, 0, index), TextExtras.Extract(macro, index+1, Text.Length(macro))); ELSE Err.Print(Fmt.F("bad macro definition \'%s\'", macro), Err.Severity.Warning); END; (* if *) END; (* while *) END CheckDefinedMacros; BEGIN FOR m := FIRST(MacroName) TO LAST(MacroName) DO EnterMacro(MacroText[m], MacroDefault[m], init := TRUE); END; (* for *) END M3DepM3Makefile.