MODULE M3DepMakefile; (***************************************************************************) (* Copyright (C) Olivetti 1989 *) (* All Rights reserved *) (* *) (* Use and copy of this software and preparation of derivative works based *) (* upon this software are permitted to any person, provided this same *) (* copyright notice and the following Olivetti warranty disclaimer are *) (* included in any copy of the software or any modification thereof or *) (* derivative work therefrom made by any person. *) (* *) (* This software is made available AS IS and Olivetti disclaims all *) (* warranties with respect to this software, whether expressed or implied *) (* under any law, including all implied warranties of merchantibility and *) (* fitness for any purpose. In no event shall Olivetti be liable for any *) (* damages whatsoever resulting from loss of use, data or profits or *) (* otherwise arising out of or in connection with the use or performance *) (* of this software. *) (***************************************************************************) (* Copyright (C) 1990, Digital Equipment Corporation *) (* All rights reserved. *) (* See the file COPYRIGHT for a full description. *) (* Modified to generate makefiles for SRC Modula-3 *) IMPORT CharType, PathNameStream, Fmt, HashText, IO, IOErr, Err, FileOp, M3Extension, M3Assert, M3Env, M3AST_AS, M3Args, M3CId, M3CUnit, M3CUnit_priv, M3Context, M3Conventions, M3CLiteral, M3DepMFTool, M3DepM3Path, M3LDepends, 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, CC, CFLAGS, IDIRS}; CONST MacroText = ARRAY MacroName OF TEXT {"CPP", "M3CPPFLAGS", "M3C", "M3L", "M3CFLAGS", "M3LFLAGS", "M3LOBJS", "M3LLIBS", "CC", "CFLAGS", "IDIRS"}; MacroDefault = ARRAY MacroName OF TEXT {"/lib/cpp", "", "m3", "$(M3C)", "", "", "", "", "cc", "", "."}; LIBDIRFLAG = 1024; (* used to indicate an archive file in a directory *) CompiledInterfaces = M3CUnit.TypeSet{M3CUnit.Type.Interface, M3CUnit.Type.Interface_gen_ins}; CompiledModules = M3CUnit.TypeSet{M3CUnit.Type.Module, M3CUnit.Type.Module_gen_ins}; 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(M3DepMFTool.Get()) THEN RETURN -1; END; IF M3Args.GetFlag(M3DepMFTool.Get(), M3DepMFTool.Gdebug_Arg) THEN EnterMacro(MacroText[MacroName.M3CFLAGS], MacroDefault[MacroName.M3CFLAGS] & " -g"); EnterMacro(MacroText[MacroName.M3LFLAGS], MacroDefault[MacroName.M3LFLAGS] & " -g"); EnterMacro(MacroText[MacroName.CFLAGS], MacroDefault[MacroName.CFLAGS] & " -g"); END; CheckDefinedMacros(); VAR fileName := M3Args.GetString(M3DepMFTool.Get(), M3DepMFTool.MakeFile_Arg); backingStream: IO.Stream; wrapStream: IO.Stream; BEGIN TRY (* except IO.Error *) IF fileName = NIL THEN fileName := M3DepMFTool.MakeFileDefault; END; IF Text.Equal(fileName, M3DepMFTool.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, M3DepMFTool.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*) TYPE M3LDependsClosure = M3LDepends.Closure OBJECT OVERRIDES callback := CheckDepends; END; PROCEDURE DoMakeFile(c: M3Context.T; p: M3DepM3Path.T; fileName: TEXT; s: IO.Stream; dir: TEXT) RAISES {}= VAR mainMods := SList.T{}; verbose := M3Args.GetFlag(M3DepMFTool.Get(), M3DepMFTool.Verbose_Arg); BEGIN stream_g := s; (* calculate program modules (if any) *) IF NOT M3Args.GetFlag(M3DepMFTool.Get(), M3DepMFTool.LocalModulesOnly_Arg) THEN mainMods := ThisDirOnly(M3LMain.Module(c, NIL), dir); IF mainMods.head # NIL THEN (* setup for m3l commands *) IF verbose THEN Err.Print("computing main program module dependency list", Err.Severity.Comment); END; (* if *) M3LDepends.Set( (* set pl_dependson_s *) c, NEW(M3LDependsClosure)); END; (* if main mods exist *) END; (* if not -nmm *) IF verbose THEN Err.Print(Fmt.F("writing %s", fileName), Err.Severity.Comment); END; (* if *) (* start printing things out *) MakefilePreamble(); DIRMacros(p); (* targets *) FilesMacro(c, M3Extension.T.IObj, dir); FilesMacro(c, M3Extension.T.MObj, dir); (* sources *) FilesMacro(c, M3Extension.T.Int, dir); FilesMacro(c, M3Extension.T.Mod, dir); ExternalMacros(c, dir); PROGSMacro(c, mainMods); AllTarget(c, M3Args.GetString(M3DepMFTool.Get(), M3DepMFTool.AR_Arg)); ProgramTargets(c, p, mainMods); M3Context.Apply(c, NEW(DirClosure, dir := dir, callback := ObjTarget), findStandard := FALSE); WorldTarget(p); 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 CheckDepends( cl: M3LDependsClosure; m: M3AST_AS.Module; i: M3AST_AS.Interface) : BOOLEAN RAISES {}= (* callback for M3LDepends.Set. Called when i is on the sm_import_s of m. * This is a varient on the default one, which always returns TRUE. * This one has nothing ever depend on 'Main'. *) BEGIN (* if i is main, false *) RETURN NOT Text.Equal( M3LMain.DefaultMain(), M3CId.ToText(i.as_id.lx_symrep)); END CheckDepends; (*PRIVATE*) PROCEDURE MakefilePreamble() RAISES {}= BEGIN Put(Fmt.F( "# Makefile generated by m3depmf version %s - for DEC SRC Modula-3\n\n", M3DepMFTool.Version)); Put("# Commands and flags\n"); FOR m := FIRST(MacroName) TO LAST(MacroName) DO Put(Fmt.F("%s = %s\n", MacroText[m], GetMacro(m))); END; (* for *) Put("# Stuff for recursive make\n"); Put("RECURTARG = all\n"); Put("MAKEDONE = ++makedone\n"); Put("RECURMAKE = ++recurmake\n"); Put("PWDX = /bin/pwd # (PWD often set by shell...)\n"); Put("RM = /bin/rm\n"); Put("TEST = /bin/test\n"); Put("TOUCH = /usr/bin/touch\n\n"); (* rules *) Put(".SUFFIXES:\t# override defaults\n"); Put(Fmt.F(".SUFFIXES: .%s .%s .%s", M3Extension.ToText(M3Extension.T.MObj), M3Extension.ToText(M3Extension.T.Mod), M3Extension.ToText(M3Extension.T.ModPp))); Put(Fmt.F(" .%s .%s .%s", M3Extension.ToText(M3Extension.T.IObj), M3Extension.ToText(M3Extension.T.Int), M3Extension.ToText(M3Extension.T.IntPp))); (* other language extensions *) Put(Fmt.F(" .c .o\n")); Put(Fmt.F(".%s.%s:\n", M3Extension.ToText(M3Extension.T.ModPp), M3Extension.ToText(M3Extension.T.Mod))); Put("\t$(CPP) -P -C $(M3CPPFLAGS) $< $@\n"); Put(Fmt.F(".%s.%s:\n", M3Extension.ToText(M3Extension.T.Mod), M3Extension.ToText(M3Extension.T.MObj))); Put("\t$(M3C) -c -D -D$(DDIRS) $(M3CFLAGS) $<\n"); Put(Fmt.F(".%s.%s:\n", M3Extension.ToText(M3Extension.T.IntPp), M3Extension.ToText(M3Extension.T.Int))); Put("\t$(CPP) -P -C $(M3CPPFLAGS) $< $@\n"); Put(Fmt.F(".%s.%s:\n", M3Extension.ToText(M3Extension.T.Int), M3Extension.ToText(M3Extension.T.IObj))); Put("\t$(M3C) -c -D -D$(DDIRS) $(M3CFLAGS) $<\n"); (* other language rules *) Put(".c.o:\n"); Put("\t$(CC) $(CFLAGS) -c -I$(IDIRS) $<\n"); Put("\n"); END MakefilePreamble; (*PRIVATE*) PROCEDURE DIRMacros(m3Path: M3DepM3Path.T) RAISES {}= CONST InitialDDirs = "DDIRS = ."; VAR i: INTEGER := 0; refI: REF INTEGER; hid: HashText.Id; name: M3Path.Elem; problem: INTEGER; localDir := -1; ddirs := InitialDDirs; BEGIN Put("# Directories (except .) on the m3path\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; (* SRC library fixup *) IF StandardRepository(name.text) OR M3DepM3Path.DirObjLib(m3Path, name) # NIL THEN INC(refI^, LIBDIRFLAG); END; HashText.Associate(dirTable_g, hid, refI); IF ContainsSources(m3Path, name) THEN (* Add to search path *) Put(Fmt.F("DIR%s = %s\n", Fmt.Int(i), name.unexpanded)); 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); Put("\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; targetsOnly := TRUE; dir: TEXT := "!@!"; END; CONST ModuleExts = M3Extension.TSet{ M3Extension.T.Mod, M3Extension.T.ModG, 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.IntG, 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; targetsOnly := TRUE ) RAISES {} = BEGIN Put(Fmt.F("%s =", ExtMac(ext, targetsOnly))); M3Context.Apply( c, NEW(FilesMacroClosure, callback := GenFile, ext := ext, targetsOnly := targetsOnly, dir := dir), findStandard := FALSE); Put("\n\n"); END FilesMacro; (*PRIVATE*) PROCEDURE GenFile( cl: FilesMacroClosure; ut: M3CUnit.Type; name: Text.T; cu: M3AST_AS.Compilation_Unit) RAISES {}= VAR file: TEXT; void: INTEGER; BEGIN file := M3CUnit.TextName(cu.fe_uid); IF Text.Equal(M3DepM3Path.RealHead(file), cl.dir) AND ((ut IN CompiledInterfaces AND cl.ext IN InterfaceExts) OR (ut IN CompiledModules AND cl.ext IN ModuleExts)) THEN Put(Fmt.F(" %s", MakeName(PathName.Extend( file, M3Extension.ToText(cl.ext)), void))); 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 Put("PROGS ="); WHILE mod # NIL DO Put(Fmt.F(" %s", ExtName(mod.cu, M3Extension.T.Exe))); mod := mod.next; END; Put("\n\n"); END PROGSMacro; (*PRIVATE*) PROCEDURE AllTarget(c: M3Context.T; archive: TEXT) RAISES {} = VAR intObjs := ExtMac(M3Extension.T.IObj); modObjs := ExtMac(M3Extension.T.MObj); extObjs := ExternalMac(FALSE); BEGIN IF archive # NIL THEN archive := "lib" & archive & ".a"; END; Put("all: preMakefile intobjs modobjs extobjs "); IF archive # NIL THEN Put(Fmt.F("%s ", archive)) END; Put("postMakefile $(PROGS)\n\n"); Put(Fmt.F("intobjs: $(%s)\n\n", intObjs)); Put(Fmt.F("modobjs: $(%s)\n\n", modObjs)); Put(Fmt.F("extobjs: $(%s)\n\n", extObjs)); IF archive # NIL THEN Put(Fmt.F("%s: $(%s) $(%s) $(%s)\n", archive, intObjs, modObjs, extObjs)); Put(Fmt.F("\t$(M3C) $(%s) $(%s) $(%s) -a %s\n", intObjs, modObjs, extObjs, archive)); END; PrePostMakefile("pre"); PrePostMakefile("post"); END AllTarget; PROCEDURE PrePostMakefile(t: TEXT) RAISES {}= VAR oldWrap: BOOLEAN; BEGIN Put(Fmt.F("%sMakefile:\n", t)); oldWrap := WrapStream.Set(stream_g, FALSE); Put(Fmt.F("\t-@if $(TEST) -f Makefile.%s ; ", t)); Put(Fmt.F("then $(MAKE) -f Makefile.%s ; else exit 0; fi\n", t)); oldWrap := WrapStream.Set(stream_g, oldWrap); Put("\n"); END PrePostMakefile; (*PRIVATE*) PROCEDURE ProgramTargets( c: M3Context.T; p: M3DepM3Path.T; mainMods: SList.T) RAISES {}= (* Print the dependencies and link commands for the program targets (XX.out). *) VAR mod: M3LMain.CuElem; iter: SeqM3AST_AS_Module.Iter; iter2: SeqM3AST_AS_Used_interface_id.Iter; used_intf_id: M3AST_AS.Used_interface_id; depends: M3AST_AS.Module; oldWrap: BOOLEAN; intTable: HashText.Table; intTableId: HashText.Id; intTableIter: HashText.Iter; intTableRA: REFANY; intTableTEXT: TEXT; PROCEDURE DoLibraries(forLinkCommand: BOOLEAN) RAISES {}= VAR dir: M3Path.Elem := M3DepM3Path.Dirs(p).head; BEGIN WHILE dir # NIL DO VAR dl := M3DepM3Path.DirObjLib(p, dir); void: INTEGER; BEGIN IF dl # NIL THEN Put(" "); IF forLinkCommand THEN Put(Fmt.F("-L%s ", MakeName(dir.unexpanded, void))); (* take the "lib" off the library name *) VAR ln := PathName.Name(dl); BEGIN Put(Fmt.F("-l%s", Text.Sub(ln, 3, Text.Length(ln)-3))); END; ELSE Put(MakeName(PathName.Concat(dir.unexpanded, dl), void)); END; END; END; dir := dir.next; END; END DoLibraries; PROCEDURE RecordImportsAndExports(depends: M3AST_AS.Module) RAISES {}= BEGIN CheckLibPut(depends); (* now record which interfaces this module uses/exports *) iter2 := SeqM3AST_AS_Used_interface_id.NewIter(depends.sm_import_s); WHILE SeqM3AST_AS_Used_interface_id.Next(iter2, used_intf_id) DO VAR name := M3CId.ToText(used_intf_id.lx_symrep); BEGIN IF NOT M3Conventions.IsStandard(name) THEN IF HashText.Enter(intTable, name,intTableId) THEN HashText.Associate(intTable, intTableId, used_intf_id); END; END; END; END; (* while *) iter2 := SeqM3AST_AS_Used_interface_id.NewIter(depends.sm_export_s); WHILE SeqM3AST_AS_Used_interface_id.Next(iter2, used_intf_id) DO IF HashText.Enter(intTable, M3CId.ToText(used_intf_id.lx_symrep), intTableId) THEN HashText.Associate(intTable, intTableId, used_intf_id); END; END; (* while *) END RecordImportsAndExports; PROCEDURE CheckLibPut(unit: M3AST_AS.UNIT) RAISES {}= VAR dirX: INTEGER; xext: TEXT; ext: M3Extension.T; BEGIN IF ISTYPE(unit, M3AST_AS.Interface) THEN ext := M3Extension.T.IObj; ELSE ext := M3Extension.T.MObj; END; xext := XExtName(unit.sm_comp_unit, ext, dirX); IF dirX < LIBDIRFLAG THEN Put(Fmt.F(" %s", xext)); END; END CheckLibPut; BEGIN mod := mainMods.head; WHILE mod # NIL DO intTable := HashText.New(256); (* The dependencies, first on self *) Put(Fmt.F( "%s:", ExtName(mod.cu, M3Extension.T.Exe))); (* record imports/exports for self *) RecordImportsAndExports(mod.cu.as_root); (* record imports/exports for dependent modules *) iter := SeqM3AST_AS_Module.NewIter( NARROW(mod.cu.as_root, M3AST_AS.Module).pl_dependson_s); WHILE SeqM3AST_AS_Module.Next(iter, depends) DO RecordImportsAndExports(depends); END; (* while *) (* OK. now list the dependencies on the compiled interfaces, avoiding things in libraries *) intTableIter := HashText.NewIterator(intTable); WHILE HashText.Next(intTableIter, intTableTEXT, intTableRA) DO used_intf_id := NARROW(intTableRA, M3AST_AS.Used_interface_id); IF used_intf_id.sm_def # NIL THEN CheckLibPut( NARROW(used_intf_id.sm_def, M3AST_AS.Interface_id).sm_spec); ELSE EVAL HashText.Lookup(intTable, intTableTEXT, intTableId); HashText.Associate(intTable, intTableId, NIL); END; END; (* while *) (* Record dependencies on the library archives. *) DoLibraries(FALSE); Put("\n"); (* The link command *) oldWrap := WrapStream.Set(stream_g, FALSE); Put(Fmt.F("\t$(M3L)")); Put(Fmt.F(" -o %s", ExtName(mod.cu, M3Extension.T.Exe))); Put(" $(M3LFLAGS) $(M3LOBJS)"); Put(Fmt.F(" %s", ExtName(mod.cu, M3Extension.T.MObj))); intTableIter := HashText.NewIterator(intTable); WHILE HashText.Next(intTableIter, intTableTEXT, intTableRA) DO used_intf_id := NARROW(intTableRA, M3AST_AS.Used_interface_id); IF used_intf_id # NIL THEN CheckLibPut(NARROW(used_intf_id.sm_def, M3AST_AS.Interface_id).sm_spec); END; END; (* while *) iter := SeqM3AST_AS_Module.NewIter( NARROW(mod.cu.as_root, M3AST_AS.Module).pl_dependson_s); WHILE SeqM3AST_AS_Module.Next(iter, depends) DO CheckLibPut(depends); END; (* while *) DoLibraries(TRUE); Put(" $(M3LLIBS)"); Put("\n\n"); oldWrap := WrapStream.Set(stream_g, oldWrap); mod := mod.next; END; (* while main mods *) END ProgramTargets; (*PRIVATE*) PROCEDURE ObjTarget( cl: DirClosure; ut: M3CUnit.Type; name: Text.T; cu: M3AST_AS.Compilation_Unit) RAISES {}= VAR ext: M3Extension.T; BEGIN IF IsMakeTarget(cu, cl.dir) THEN IF ut IN CompiledInterfaces THEN ext := M3Extension.T.IObj; ELSE ext := M3Extension.T.MObj; END; Put(Fmt.F("%s:", ExtName(cu, ext))); DependsSeq(cl.context, NARROW(cu.as_root, M3AST_AS.UNIT_NORMAL).sm_import_s); Put("\n\n"); END; (* if isMakeTarget *) END ObjTarget; (*PRIVATE*) PROCEDURE DependsSeq( c: M3Context.T; depends_s: SeqM3AST_AS_Used_interface_id.T) RAISES {}= VAR iter := SeqM3AST_AS_Used_interface_id.NewIter(depends_s); used_intf_id: M3AST_AS.Used_interface_id; import: M3AST_AS.Compilation_Unit; void: INTEGER; BEGIN WHILE SeqM3AST_AS_Used_interface_id.Next(iter, used_intf_id) DO IF M3Context.FindFromId( c, used_intf_id.lx_symrep, M3CUnit.Type.Interface, import) THEN IF import # M3Context.Standard() THEN Put(Fmt.F(" %s", MakeName(M3CUnit.TextName(import.fe_uid), void))); END; END; END; (* while *) END DependsSeq; (*PRIVATE*) PROCEDURE WorldTarget(m3path: M3DepM3Path.T) RAISES {}= VAR oldWrap: BOOLEAN; value: REFANY; id: HashText.Id; dirs := M3DepM3Path.Dirs(m3path); name: M3Path.Elem := dirs.head; BEGIN Put("world:\trecur recurclean\n\n"); Put("recur:\n"); oldWrap := WrapStream.Set(stream_g, FALSE); Put("\t@-if $(TEST) -f $(RECURMAKE) ; then \\\n"); Put("\t echo -n \'recursive make: directory loop involving \'; "); Put("$(PWDX) ; \\\n"); Put("\telif $(TEST) ! -f $(MAKEDONE) ; then \\\n"); Put("\t $(TOUCH) $(RECURMAKE) ; \\\n"); WHILE name # NIL DO IF NOT (name.readOnly OR IsLocalDir(name.text)) THEN M3Assert.Check(HashText.Lookup(dirTable_g, name.text, id)); Put(Fmt.F( "\t (cd $(DIR%s) ; $(MAKE) %s recur) ; \\\n", Fmt.Int(NARROW(HashText.Value(dirTable_g, id), REF INTEGER)^ MOD LIBDIRFLAG), RecurFlags())); END; name := name.next; END; Put("\t echo -n \'making $(RECURTARG) in \'; $(PWDX) ; \\\n"); Put(Fmt.F("\t $(MAKE) %s $(RECURTARG) ; \\\n", RecurFlags())); Put("\t $(TOUCH) $(MAKEDONE) ; \\\n"); Put("\tfi\n"); Put("\t@-$(RM) -f $(RECURMAKE)\n\n"); oldWrap := WrapStream.Set(stream_g, oldWrap); Put("recurclean:\n"); oldWrap := WrapStream.Set(stream_g, FALSE); Put("\t@-$(RM) -f $(RECURMAKE)\n"); Put("\t@-if $(TEST) -f $(MAKEDONE) ; then \\\n"); Put("\t $(RM) -f $(MAKEDONE) ; \\\n"); name := dirs.head; WHILE name # NIL DO IF NOT (name.readOnly OR IsLocalDir(name.text)) THEN M3Assert.Check(HashText.Lookup(dirTable_g, name.text, id)); Put(Fmt.F( "\t (cd $(DIR%s) ; $(MAKE) %s recurclean) ; \\\n", Fmt.Int(NARROW(HashText.Value(dirTable_g, id), REF INTEGER)^ MOD LIBDIRFLAG), RecurFlags())); END; name := name.next; END; Put("\tfi\n\n"); oldWrap := WrapStream.Set(stream_g, oldWrap); Put("listdirs: # RECURTARG=listdirs to list all directories\n\n"); Put("recurfix:\n"); Put("\t$(MAKE) world RECURTARG=listdirs\n"); END WorldTarget; (*PRIVATE*) PROCEDURE RecurFlags(): Text.T RAISES {} = BEGIN RETURN "CPP=\'$(CPP)\' M3CPPFLAGS=\'$(M3CPPFLAGS)\' M3C=\'$(M3C)\' " & "M3CFLAGS=\'$(M3CFLAGS)\' M3LFLAGS=\'$(M3LFLAGS)\' " & "RECURTARG=\'$(RECURTARG)\' RECURMAKE=\'$(RECURMAKE)\' " & "PWDX=\'$(PWDX)\' RM=\'$(RM)\' TOUCH=\'$(TOUCH)\' TEST=\'$(TEST)\' " & "MAKEDONE=\'$(MAKEDONE)\' M3L=\'$(M3L)\' AR=\'$(AR)\' " & "M3AR=\'$(M3AR)\' CC=\'$(CC)\' RANLIB=\'$(RANLIB)\'" END RecurFlags; (*PRIVATE*) PROCEDURE MakefileCoda(errors: BOOLEAN) RAISES {}= VAR oldWrap: BOOLEAN; BEGIN Put("clean:\n"); oldWrap := WrapStream.Set(stream_g, FALSE); Put(Fmt.F( "\t-$(RM) -f $(%s) $(%s) $(PROGS) $(RECURMAKE) $(MAKEDONE)\n\n", ExtMac(M3Extension.T.IObj), ExtMac(M3Extension.T.MObj))); oldWrap := WrapStream.Set(stream_g, oldWrap); 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*) <*INLINE*> PROCEDURE ArchiveName(dir: TEXT): TEXT RAISES {}= BEGIN RETURN PathName.Concat(dir, PathName.Extend("lib" & PathName.Tail(dir), "a")); END ArchiveName; (*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(M3DepMFTool.Get(), M3DepMFTool.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 M3DepMakefile.