3 IMPORT Kernel
, Files
, Log
, Strings
, DswOpts
, DswProcs
, DswDocuments
, DevCPM
, DevCPT
, DevCPR
, DevCPS
;
15 null
= 0; times
= 1; slash
= 2; div
= 3; mod
= 4;
16 and
= 5; plus
= 6; minus
= 7; or
= 8; eql
= 9;
17 neq
= 10; lss
= 11; leq
= 12; gtr
= 13; geq
= 14;
18 in
= 15; is
= 16; arrow
= 17; dollar
= 18; period
= 19;
19 comma
= 20; colon
= 21; upto
= 22; rparen
= 23; rbrak
= 24;
20 rbrace
= 25; of
= 26; then
= 27; do
= 28; to
= 29;
22 lparen
= 40; lbrak
= 41; lbrace
= 42; becomes
= 44;
23 number
= 45; nil
= 46; string
= 47; ident
= 48; semicolon
= 49;
24 bar
= 50; end
= 51; else
= 52; elsif
= 53; until
= 54;
25 if
= 55; case
= 56; while
= 57; repeat
= 58; for
= 59;
26 loop
= 60; with
= 61; exit
= 62; return
= 63; array
= 64;
27 record
= 65; pointer
= 66; begin
= 67; const
= 68; type
= 69;
28 var
= 70; out
= 71; procedure
= 72; close
= 73; import
= 74;
29 module
= 75; eof
= 76;
31 (* module state flags *)
32 imported
= 0; trace
= 1; hasObj
= 2; hasSym
= 3; hasErrors
= 4;
33 library
= 5; force
= 6;
40 Name
= ARRAY 256 OF CHAR;
42 String
= POINTER TO ARRAY OF CHAR;
44 Selector
= POINTER TO RECORD
50 Module
= POINTER TO RECORD
54 depth
: INTEGER; (* 0: leaf, MAX: root *)
55 dir
: DevCPM
.Directory
;
57 imp
: ARRAY maxImps
OF Module
;
59 worker
: DswProcs
.Process
;
62 Library
= POINTER TO RECORD
69 auto
, trap
, clean
, symonly
, nocode1
, nocode2
: BOOLEAN;
70 exe
, target
, base
: String
;
75 mno
, rno
: INTEGER; (* num modules *)
76 modList
, lnkList
, cmpList
: ARRAY maxImps
OF Module
;
77 def
: Selector
; (* with head, global list of selectors *)
78 dirList
: DevCPM
.Directory
;
81 PROCEDURE Error (IN str
, p0
, p1
: ARRAY OF CHAR; i2
: INTEGER);
82 VAR p2
: ARRAY 32 OF CHAR;
84 Strings
.IntToString(i2
, p2
);
85 Log
.String("cpmake: "); Log
.ParamMsg(str
, p0
, p1
, p2
); Log
.Ln
;
92 (* --------- options --------- *)
94 PROCEDURE IdentLen (IN s
: ARRAY OF CHAR): INTEGER;
98 IF Strings
.IsIdentStart(s
[0]) THEN
99 REPEAT INC(i
) UNTIL ~Strings
.IsIdent(s
[i
])
104 PROCEDURE Define (n
: ARRAY OF CHAR; overwrite
: BOOLEAN);
105 VAR i
: INTEGER; v
: BOOLEAN; s
: Selector
;
109 IF ~
((n
[i
] = 0X
) OR (((n
[i
] = "+") OR (n
[i
] = "-")) & (n
[i
+ 1] = 0X
))) THEN
110 Error("expectd + or - after selector identifier (^0)", n
, "", 0)
112 v
:= n
[i
] # "-"; n
[i
] := 0X
; s
:= def
;
113 WHILE (s
.next
# NIL) & (s
.next
.name$
# n$
) DO s
:= s
.next
END;
116 Strings
.StringToUtf8(n
, s
.next
.name
, i
);
122 ELSE Error("selector '^0' have invalid identifier", n
, "", 0)
126 PROCEDURE DefineNew (IN n
: ARRAY OF CHAR);
131 PROCEDURE AddModule (IN n
: ARRAY OF CHAR; selectors
: Selector
; dir
: DevCPM
.Directory
);
132 VAR i
, res
: INTEGER; m
: Module
;
135 IF (i
# 0) & (n
[i
] = 0X
) THEN
137 WHILE (i
< mno
) & (modList
[i
].name$
# n$
) DO INC(i
) END;
140 Strings
.StringToUtf8(n
, m
.name
, res
);
147 Error("invalid module name", "", "", 0)
151 PROCEDURE AddLibrary (IN key
, val
: ARRAY OF CHAR);
155 WHILE (x
# NIL) & (x
.name$
# key$
) DO x
:= x
.next
END;
156 IF x
# NIL THEN x
.library
:= val$
157 ELSE NEW(x
); x
.name
:= key$
; x
.library
:= val$
; x
.next
:= libList
; libList
:= x
161 PROCEDURE AddLib (IN s
: ARRAY OF CHAR);
162 VAR key
, val
: Name
; i
, j
: INTEGER;
165 WHILE (s
[i
] # 0X
) & (s
[i
] # "=") DO
181 IF key
# "" THEN AddLibrary(key
, val
)
182 ELSE Error("empty library key", "", "", 0)
186 PROCEDURE FindLib (IN key
: ARRAY OF CHAR): Library
;
190 WHILE (x
# NIL) & (x
.name$
# key$
) DO x
:= x
.next
END;
194 PROCEDURE StrToInt (IN s
: ARRAY OF CHAR; def
: INTEGER): INTEGER;
197 Strings
.StringToInt(s
, x
, res
);
199 Error("invalid integer value", s
, "", 0);
205 PROCEDURE NewStr (IN s
: ARRAY OF CHAR): String
;
208 NEW(p
, LEN(s$
) + 1); p^
:= s$
;
214 Log
.String("Usage: cpmake [options] module..."); Log
.Ln
;
215 Log
.String("Options:"); Log
.Ln
;
216 Log
.String(" -a Enable automatic dependency resolution"); Log
.Ln
;
217 Log
.String(" -b Do not compile modules"); Log
.Ln
;
218 Log
.String(" -x Do not link objects"); Log
.Ln
;
219 Log
.String(" -c Remove all generated files"); Log
.Ln
;
220 Log
.String(" -s Generate symbol files only"); Log
.Ln
;
221 Log
.String(" -d selector Add selector"); Log
.Ln
;
222 Log
.String(" -r lib[=s] Replace library name on link stage"); Log
.Ln
;
223 Log
.String(" -t target Specify target rules"); Log
.Ln
;
224 Log
.String(" -o file Generate object file"); Log
.Ln
;
225 Log
.String(" -j num Specifies the number of jobs to run simultaneously"); Log
.Ln
;
226 Log
.String(" -f path Specifies path to directory with targets"); Log
.Ln
;
227 Log
.String(" -h Print help"); Log
.Ln
;
228 Log
.String(" -v Print version"); Log
.Ln
;
234 exe
:= NIL; auto
:= FALSE
; jobs
:= 1; def
.next
:= NIL; mno
:= 0; rno
:= 0;
235 target
:= NewStr("default"); base
:= NewStr("cprules");
237 CASE DswOpts
.GetOpt("acbxd:sgGo:t:j:f:r:h") OF
239 |
"b": nocode1
:= TRUE
240 |
"x": nocode2
:= TRUE
243 |
"G": Kernel
.intTrap
:= TRUE
244 |
"s": symonly
:= TRUE
245 |
"f": base
:= DswOpts
.str
246 |
"d": Define(DswOpts
.str
, TRUE
)
247 |
"r": AddLib(DswOpts
.str
)
249 |
"j": jobs
:= MIN(MAX(StrToInt(DswOpts
.str
, 1), 1), maxJobs
)
250 |
"o": exe
:= DswOpts
.str
251 |
"t": target
:= DswOpts
.str
252 |
"v": Log
.String(version
); Log
.Ln
; Kernel
.Quit(0)
253 |
":": Error("missing argument for option -^0", DswOpts
.str
, "", 0)
254 |
"?": Error("unknown option -^0", DswOpts
.str
, "", 0)
255 |
"$": AddModule(DswOpts
.str
, def
, dirList
)
261 PROCEDURE ReadLines (loc
: Files
.Locator
; IN name
: Files
.Name
; p
: PROCEDURE (IN s
: ARRAY OF CHAR));
262 VAR s
: Name
; m
: DswDocuments
.Model
; r
: DswDocuments
.Reader
; i
, res
: INTEGER;
264 ASSERT(loc
# NIL, 20);
265 ASSERT(name
# "", 21);
267 DswDocuments
.Open(loc
, name
, m
, res
);
269 r
:= m
.NewReader(NIL);
274 WHILE ~r
.eot
& (r
.char
<= 20X
) DO r
.Read
END;
275 WHILE ~r
.eot
& (r
.char
> 20X
) DO s
[i
] := r
.char
; r
.Read
; INC(i
) END;
276 IF i
# 0 THEN s
[i
] := 0X
; p(s
) END
281 PROCEDURE CheckParams
;
282 VAR loc
: Files
.Locator
;
284 IF (exe
# NIL) & (exe^
= "") THEN
285 Error("specified empty file name for exe", "", "", 0)
288 Error("specified empty rule", "", "", 0)
291 Error("specified empty path to cpmake rules", "", "", 0)
293 loc
:= Files
.dir
.This(base
).This(target
);
294 ReadLines(loc
, "defines", DefineNew
);
295 ReadLines(loc
, "libs", AddLib
)
298 (* --------- loader --------- *)
300 PROCEDURE Import (m
: Module
; IN name
: DevCPT
.Name
);
301 VAR i
, j
: INTEGER; imp
: Module
;
304 ASSERT(name
# "", 21);
305 IF debugImport
THEN Log
.String(" import "); Log
.String(name$
) END;
306 IF name
= "SYSTEM" THEN INCL(DevCPM
.options
, DevCPM
.sysImp
)
307 ELSIF name
= "COM" THEN INCL(DevCPM
.options
, DevCPM
.com
)
308 ELSIF name
= "JAVA" THEN INCL(DevCPM
.options
, DevCPM
.java
)
310 IF debugImport
THEN Log
.Char(" ") END;
311 i
:= 0; (* find module in local list *)
312 WHILE (i
< m
.mno
) & (m
.imp
[i
].name$
# name$
) DO INC(i
) END;
314 j
:= 0; (* find module in global list *)
315 WHILE (j
< mno
) & (modList
[j
].name$
# name$
) DO INC(j
) END;
318 Log
.String("module " + name
+ " required before " + m
.name
); Log
.Ln
; INC(werr
)
320 NEW(imp
); imp
.name
:= name$
;
321 modList
[mno
] := imp
; INC(mno
)
325 m
.imp
[m
.mno
] := imp
; INC(m
.mno
)
329 IF debugImport
THEN Log
.Ln
END;
332 PROCEDURE ParseModule (m
: Module
);
333 VAR sym
: BYTE; SelfName
, impName
, aliasName
: DevCPT
.Name
;
335 PROCEDURE err (n
: SHORTINT);
339 PROCEDURE CheckSym(s
: SHORTINT);
341 IF sym
= s
THEN DevCPS
.Get(sym
) ELSE DevCPM
.err(s
) END
345 IF debugImport
THEN Log
.String("module " + m
.name
); Log
.Ln
END;
346 DevCPS
.Init
; DevCPS
.Get(sym
);
347 IF sym
= module
THEN DevCPS
.Get(sym
) ELSE err(16) END;
349 SelfName
:= DevCPS
.name$
; DevCPS
.Get(sym
);
351 INCL(DevCPM
.options
, DevCPM
.interface
); DevCPS
.Get(sym
);
352 IF sym
= eql
THEN DevCPS
.Get(sym
)
353 ELSE INCL(DevCPM
.options
, DevCPM
.noCode
)
355 IF sym
= string
THEN INCL(m
.flags
, library
); m
.library
:= DevCPS
.str$
; DevCPS
.Get(sym
)
361 IF sym
= import
THEN DevCPS
.Get(sym
);
364 aliasName
:= DevCPS
.name$
; impName
:= aliasName$
; DevCPS
.Get(sym
);
365 IF sym
= becomes
THEN DevCPS
.Get(sym
);
366 IF sym
= ident
THEN impName
:= DevCPS
.name$
; DevCPS
.Get(sym
) ELSE err(ident
) END
371 IF sym
= comma
THEN DevCPS
.Get(sym
)
372 ELSIF sym
= ident
THEN err(comma
)
378 LOOP (* preprocessor must read module fully *)
384 IF DevCPS
.name
# SelfName
THEN err(4) END;
406 PROCEDURE CheckModule (m
: Module
; source
: String
; OUT ok
: BOOLEAN);
410 DevCPM
.symList
:= m
.dir
;
411 (*DevCPM.codePath := m.outcode;*)
412 (*DevCPM.symPath := m.outsym;*)
413 DevCPM
.name
:= m
.name$
;
414 INCL(DevCPM
.options
, DevCPM
.comAware
);
416 INCL(DevCPM
.options
, DevCPM
.trap
)
418 (*IF oberon IN m.opts THEN INCL(DevCPM.options, DevCPM.oberon) END;*)
422 DevCPR
.Set(s
.name
, s
.value
);
428 IF DevCPR
.used
THEN INCL(m
.flags
, force
) END;
435 PROCEDURE GetModText (IN base
, sub
, name
: Files
.Name
; OUT path
: Files
.Name
; OUT text
: DswDocuments
.Model
);
436 VAR res
: INTEGER; loc
: Files
.Locator
;
438 ASSERT(name
# "", 21);
440 loc
:= Files
.dir
.This(base
);
441 IF base
= "" THEN path
:= name
442 ELSE path
:= base
+ "/" + name
445 loc
:= Files
.dir
.This(base
).This(sub
).This(modDir
);
446 IF base
= "" THEN path
:= sub
+ "/" + modDir
+ "/" + name
447 ELSE path
:= base
+ "/" + sub
+ "/" + modDir
+ "/" + name
450 DswDocuments
.Open(loc
, name
, text
, res
)
453 PROCEDURE Open (IN base
, sub
, name
: Files
.Name
; OUT path
: Files
.Name
; OUT text
: DswDocuments
.Model
);
454 VAR cp
, odc
: Files
.Name
;
456 ASSERT(name
# "", 20);
457 Files
.dir
.GetFileName(name
, "cp", cp
); (* !!! *)
458 GetModText(base
, sub
, cp
, path
, text
);
460 Files
.dir
.GetFileName(name
, "odc", odc
); (* !!! *)
461 GetModText(base
, sub
, odc
, path
, text
);
462 IF (text
= NIL) & (sub
= "") THEN
463 GetModText(base
, sysDir
, cp
, path
, text
);
465 GetModText(base
, sysDir
, odc
, path
, text
);
474 PROCEDURE GetSource (IN modName
: ARRAY OF CHAR; list
: DevCPM
.Directory
; OUT path
: Files
.Name
; OUT s
: String
);
476 sub
, name
: Files
.Name
;
477 base
: DevCPM
.Directory
;
478 text
: DswDocuments
.Model
;
479 r
: DswDocuments
.Reader
;
482 s
:= NIL; path
:= ""; base
:= list
;
483 Kernel
.SplitName(modName
, sub
, name
);
484 Open("", sub
, name
, path
, text
);
485 WHILE (text
= NIL) & (base
# NIL) DO
486 ASSERT(base
.legacy
, 100);
487 Open(base
.path
, sub
, name
, path
, text
);
491 NEW(s
, text
.Length() + 1);
493 r
:= text
.NewReader(NIL);
494 FOR i
:= 0 TO text
.Length() - 1 DO
495 r
.Read
; s
[i
] := r
.char
501 PROCEDURE Trace (m
, parent
: Module
; VAR lno
: INTEGER);
504 IF ~
(trace
IN m
.flags
) THEN
505 INCL(m
.flags
, trace
);
506 FOR i
:= 0 TO m
.mno
- 1 DO
507 Trace(m
.imp
[i
], m
, lno
);
508 m
.depth
:= MAX(m
.depth
, m
.imp
[i
].depth
+ 1)
510 IF ~
(imported
IN m
.flags
) THEN
511 INCL(m
.flags
, imported
);
517 Error("recursive import of ^0 in ^1", m
.name$
, parent
.name$
, 0)
522 VAR i
, j
: INTEGER; m
: Module
;
524 ASSERT((mno
= 0) OR (lnkList
[0] # NIL), 20);
530 WHILE (j
>= 0) & (cmpList
[j
].depth
> m
.depth
) DO
531 cmpList
[j
+ 1] := cmpList
[j
];
540 VAR i
, j
, num
: INTEGER; m
: Module
; src
: String
; ok
: BOOLEAN;
543 WHILE (err
= 0) & (i
< mno
) DO
545 GetSource(m
.name$
, m
.dir
, m
.odc
, src
);
547 CheckModule(m
, src
, ok
);
548 IF ~ok
THEN INC(err
) END
550 Error("unable to open module ^0", m
.name$
, "", 0)
556 FOR i
:= 0 TO rno
- 1 DO
557 Trace(modList
[i
], modList
[i
], num
)
559 ASSERT((err
# 0) OR (num
= mno
), 100);
562 Log
.String("Parallel depth:"); Log
.Ln
;
563 FOR i
:= 0 TO mno
- 1 DO
564 Log
.String(" " + cmpList
[i
].name
); Log
.Int(cmpList
[i
].depth
); Log
.Ln
;
569 PROCEDURE IsCompiled (m
: Module
): BOOLEAN;
570 CONST target
= {hasSym
, hasObj
};
571 VAR i
: INTEGER; ready
: BOOLEAN;
575 ready
:= ~
(hasErrors
IN m
.flags
) & (m
.flags
* target
= target
);
576 WHILE ready
& (i
< m
.mno
) DO
577 ready
:= IsCompiled(m
.imp
[i
]);
583 PROCEDURE Ready (m
: Module
): BOOLEAN;
584 CONST target
= {hasSym
, hasObj
};
585 VAR i
: INTEGER; ready
: BOOLEAN;
588 ready
:= ~
(hasErrors
IN m
.flags
) & (m
.flags
* target
# target
) & (m
.worker
= NIL);
589 WHILE ready
& (i
< m
.mno
) DO
590 ready
:= IsCompiled(m
.imp
[i
]);
596 PROCEDURE PrepareCompilerDeps (m
: Module
; p
: DswProcs
.Process
; root
, libsOnly
: BOOLEAN);
597 VAR i
: INTEGER; s
: ARRAY 3 OF CHAR; lib
: Library
;
599 IF ~
(trace
IN m
.flags
) THEN
600 INCL(m
.flags
, trace
);
601 FOR i
:= 0 TO m
.mno
- 1 DO
602 PrepareCompilerDeps(m
.imp
[i
], p
, FALSE
, libsOnly
)
604 IF ~libsOnly
OR (library
IN m
.flags
) THEN
605 IF library
IN m
.flags
THEN s
:= "-l" ELSE s
:= "-m" END;
606 IF root
THEN s
[1] := CAP(s
[1]) END;
607 IF library
IN m
.flags
THEN
608 lib
:= FindLib(m
.library$
);
610 IF lib
.library$
# "" THEN
612 p
.PutParam(lib
.library$
)
616 p
.PutParam(m
.library$
)
625 END PrepareCompilerDeps
;
627 PROCEDURE PrepareCompiler (m
: Module
): DswProcs
.Process
;
628 VAR p
: DswProcs
.Process
; s
: Selector
; i
: INTEGER;
631 ASSERT(m
.odc
# "", 21);
632 ASSERT(m
.worker
= NIL, 22);
633 p
:= DswProcs
.dir
.New();
634 p
.Program(base
+ "/" + target
+ "/" + "build");
636 IF nocode1
THEN p
.PutParam("-b") END;
637 IF nocode2
THEN p
.PutParam("-x") END;
638 IF force
IN m
.flags
THEN p
.PutParam("-f") END;
639 IF symonly
OR (library
IN m
.flags
) THEN p
.PutParam("-s") END;
642 IF s
.value
THEN p
.PutParam("-D") ELSE p
.PutParam("-d") END;
646 FOR i
:= 0 TO m
.mno
- 1 DO
647 PrepareCompilerDeps(m
.imp
[i
], p
, TRUE
, FALSE
);
653 VAR i
, j
, num
, res
: INTEGER; ok
: BOOLEAN; m
: Module
; w
: DswProcs
.Process
;
655 IF mno
= 0 THEN RETURN END;
657 WHILE (err
= 0) & (num
< mno
) OR (j
> 0) DO
659 WHILE (err
= 0) & (i
< mno
) & (j
< jobs
) DO
662 w
:= PrepareCompiler(m
);
663 IF debugJobs
THEN Log
.String("Start job " + m
.name
) END;
666 IF debugJobs
THEN Log
.String(" ok") END;
670 IF debugJobs
THEN Log
.String(" fail") END;
671 INCL(m
.flags
, hasErrors
);
674 IF debugJobs
THEN Log
.Ln
END
678 WHILE (err
= 0) & (j
>= jobs
) OR (j
> 0) DO
680 WHILE (j
> 0) & (i
< mno
) DO
683 IF (w
# NIL) & w
.IsTerminated() THEN
685 IF debugJobs
THEN Log
.String("Stop job " + m
.name
); Log
.Int(res
); Log
.Ln
END;
687 INCL(m
.flags
, hasObj
);
688 INCL(m
.flags
, hasSym
);
691 INCL(m
.flags
, hasErrors
);
704 VAR p
: DswProcs
.Process
; ok
: BOOLEAN; i
, res
: INTEGER;
706 IF ~symonly
& (exe
# NIL) THEN
707 p
:= DswProcs
.dir
.New();
708 p
.Program(base
+ "/" + target
+ "/" + "link");
710 FOR i
:= 0 TO mno
- 1 DO
711 IF ~
(library
IN lnkList
[i
].flags
) THEN
712 p
.PutParam(lnkList
[i
].name$
)
715 FOR i
:= 0 TO mno
- 1 DO
716 IF library
IN lnkList
[i
].flags
THEN
717 PrepareCompilerDeps(lnkList
[i
], p
, FALSE
, TRUE
)
725 Error("link failed with code ^2", "", "", res
)
727 ELSE Error("link failed: unable to execute script", "", "", 0)
733 VAR p
: DswProcs
.Process
; ok
: BOOLEAN; res
: INTEGER;
736 p
:= DswProcs
.dir
.New();
737 p
.Program(base
+ "/" + target
+ "/" + "clean");
743 Error("cleanup failed with code ^2", "", "", res
)
745 ELSE Error("cleanup failed: unable to execute script", "", "", 0)
752 IF Kernel
.trapCount
= 0 THEN
769 ELSE Error("trap occured", "", "", 0)
771 IF err
= 0 THEN Kernel
.Quit(0)
778 ASSERT(def
# NIL, 100);
779 Kernel
.intTrap
:= FALSE
;