include "makedata.s7i";
include "stdio.s7i";
include "osfiles.s7i";
include "time.s7i";
include "cli_cmds.s7i";
const type: makeFlag is new enum
ignoreErrors. dontExecute, silentMode
end enum;
const type: makeFlags is set of makeFlag;
const proc: make (in string: makefile, in array string: targets, in makeFlags: flags) is forward;
const proc: doMake (inout string: parameters) is func
local
var string: aParam is "";
var string: makefile is "";
var array string: targets is 0 times "";
var makeFlags: flags is makeFlags.value;
var string: savedCurrentDir is "";
begin
savedCurrentDir := getcwd;
skipWhiteSpace(parameters);
while parameters <> "" and parameters[1] in parameter_char do
aParam := getCommandParameter(parameters);
if length(aParam) >= 2 and aParam[1] = '-' then
if aParam = "-C" then
skipWhiteSpace(parameters);
aParam := convDosPath(getCommandParameter(parameters));
chdir(aParam);
elsif aParam = "-f" then
skipWhiteSpace(parameters);
makefile := convDosPath(getCommandParameter(parameters));
elsif aParam = "-i" then
incl(flags, ignoreErrors);
elsif aParam = "-n" then
incl(flags, dontExecute);
elsif aParam = "-s" then
incl(flags, silentMode);
end if;
else
targets &:= aParam;
end if;
skipWhiteSpace(parameters);
end while;
if makefile = "" then
if fileType("makefile") = FILE_REGULAR then
makefile := "makefile";
elsif fileType("Makefile") = FILE_REGULAR then
makefile := "Makefile";
end if;
end if;
make(makefile, targets, flags);
chdir(savedCurrentDir);
end func;
const func string: expandInternalMacro (in char: macro, in ruleType: rule,
in array string: strictDependencies) is func
result
var string: expandedMacro is "";
begin
case macro of
when {'@'}:
expandedMacro := rule.target;
when {'<'}:
if length(rule.dependencies) <> 0 then
expandedMacro := rule.dependencies[1];
end if;
when {'?'}:
if length(strictDependencies) <> 0 then
expandedMacro := join(strictDependencies, " ");
end if;
when {'^'}:
if length(strictDependencies) <> 0 then
expandedMacro := join(strictDependencies, " ");
end if;
when {'+'}:
if length(rule.dependencies) <> 0 then
expandedMacro := join(rule.dependencies, " ");
end if;
end case;
end func;
const func string: applyInternalMacros (in ruleType: rule, in string: stri) is func
result
var string: macrosApplied is "";
local
const set of char: iternalMacroDesignator is {'@', '<', '?', '^', '+'};
var integer: dollarPos is 0;
var integer: pos is 0;
var string: dependency is "";
var set of string: dependencySet is (set of string).value;
var array string: strictDependencies is 0 times "";
var char: macro is ' ';
var string: suffix is "";
var string: replacement is "";
begin
for dependency range rule.dependencies do
if dependency not in dependencySet then
strictDependencies &:= dependency;
incl(dependencySet, dependency);
end if;
end for;
macrosApplied := stri;
dollarPos := pos(macrosApplied, '$');
while dollarPos <> 0 do
if dollarPos < length(macrosApplied) then
if macrosApplied[succ(dollarPos)] in iternalMacroDesignator then
macrosApplied := macrosApplied[.. pred(dollarPos)] &
expandInternalMacro(macrosApplied[succ(dollarPos)], rule, strictDependencies) &
macrosApplied[dollarPos + 2 ..];
elsif macrosApplied[succ(dollarPos)] = '(' then
pos := dollarPos + 2;
if pos <= length(macrosApplied) and macrosApplied[pos] in iternalMacroDesignator then
macro := macrosApplied[pos];
incr(pos);
if pos <= length(macrosApplied) then
if macrosApplied[pos] = ')' then
macrosApplied := macrosApplied[.. pred(dollarPos)] &
expandInternalMacro(macro, rule, strictDependencies) &
macrosApplied[succ(pos) ..];
elsif macrosApplied[pos] = ':' then
incr(pos);
suffix := "";
while pos <= length(macrosApplied) and macrosApplied[pos] <> '=' do
suffix &:= macrosApplied[pos];
incr(pos);
end while;
if pos <= length(macrosApplied) then
incr(pos);
replacement := "";
while pos <= length(macrosApplied) and macrosApplied[pos] <> ')' do
replacement &:= macrosApplied[pos];
incr(pos);
end while;
if pos <= length(macrosApplied) then
macrosApplied := macrosApplied[.. pred(dollarPos)] &
replaceSuffixes(expandInternalMacro(macro, rule, strictDependencies), suffix, replacement) &
macrosApplied[succ(pos) ..];
end if;
end if;
end if;
end if;
end if;
elsif macrosApplied[succ(dollarPos)] = '$' then
macrosApplied := macrosApplied[.. pred(dollarPos)] & macrosApplied[succ(dollarPos) ..];
end if;
end if;
dollarPos := pos(macrosApplied, '$', succ(dollarPos));
end while;
end func;
const proc: processCommands (in makeDataType: makeData, inout ruleType: rule) is func
local
var string: command is "";
var integer: commandStatus is 0;
var boolean: printCommand is TRUE;
var boolean: ignoreError is FALSE;
begin
for command range rule.commands do
command := applyInternalMacros(rule, command);
printCommand := TRUE;
ignoreError := FALSE;
if startsWith(command, "@-") or startsWith(command, "-@") then
printCommand := FALSE;
ignoreError := TRUE;
command := command[3 ..];
elsif startsWith(command, "@") then
printCommand := FALSE;
command := command[2 ..];
elsif startsWith(command, "-") then
ignoreError := TRUE;
command := command[2 ..];
end if;
command := applyMacros(makeData.macros, command, FALSE);
if command <> "" then
if printCommand and not makeData.inSilentMode or
not printCommand and not makeData.executeCommands then
writeln(command);
flush(STD_OUT);
end if;
if makeData.executeCommands then
commandStatus := processCommand(command);
if commandStatus <> 0 and not (ignoreError or makeData.doIgnoreErrors) then
writeln(" *** [" <& rule.target <& "] Error " <& commandStatus);
raise FILE_ERROR;
end if;
end if;
end if;
end for;
end func;
const func string: pattern_match (in var string: main_stri, in string: pattern) is func
result
var string: stem is "";
local
var integer: percentPos is 0;
var integer: main_index is 2;
var string: pattern_tail is "";
begin
if startsWith(main_stri, "./") then
main_stri := main_stri[3 ..];
end if;
percentPos := pos(pattern, '%');
if percentPos = rpos(pattern, '%') then
if startsWith(main_stri, pattern[.. pred(percentPos)]) and
endsWith(main_stri, pattern[succ(percentPos) ..]) then
stem := main_stri[percentPos .. length(main_stri) - length(pattern) + percentPos];
end if;
end if;
end func;
const proc: processRule (inout makeDataType: makeData, in string: targetName) is forward;
const proc: processRule (inout makeDataType: makeData, inout ruleType: rule) is func
local
var string: dependency is "";
var string: dependencyFile is "";
var string: ruleFile is "";
var time: targetTime is time.value;
var boolean: commandsNecessary is FALSE;
begin
if not rule.ruleProcessed then
for dependency range rule.dependencies do
processRule(makeData, dependency);
end for;
ruleFile := convDosPath(rule.target);
if fileType(ruleFile) = FILE_ABSENT then
processCommands(makeData, rule);
else
targetTime := getMTime(ruleFile);
for dependency range rule.dependencies do
dependencyFile := convDosPath(applyMacros(makeData.macros, dependency, FALSE));
if dependencyFile <> "" then
if fileType(dependencyFile) = FILE_ABSENT then
if makeData.executeCommands then
writeln(" *** File " <& dependencyFile <& " missing");
else
commandsNecessary := TRUE;
end if;
elsif getMTime(dependencyFile) > targetTime then
commandsNecessary := TRUE;
end if;
end if;
end for;
if commandsNecessary then
processCommands(makeData, rule);
end if;
end if;
rule.ruleProcessed := TRUE;
end if;
end func;
const proc: processPatternRule (inout makeDataType: makeData, in string: targetName,
inout ruleType: dependencyRule) is func
local
var string: ruleName is "";
var integer: ruleIndex is 0;
var string: stem is "";
var boolean: found is FALSE;
var string: ruleNameFound is "";
var integer: ruleIndexFound is 1;
var integer: numberOfRulesFound is 1;
var string: stemFound is "";
var ruleType: patternRule is ruleType.value;
var ruleType: actualRule is ruleType.value;
var string: dependency is "";
var string: actualDependency is "";
begin
for key ruleName range makeData.patternRules do
stem := pattern_match(targetName, ruleName);
if stem <> "" then
if not found or length(stem) < length(stemFound) then
stemFound := stem;
ruleNameFound := ruleName;
end if;
found := TRUE;
end if;
end for;
if found then
if length(makeData.patternRules[ruleNameFound]) <> 1 then
numberOfRulesFound := 0;
for key ruleIndex range makeData.patternRules[ruleNameFound] do
for dependency range makeData.patternRules[ruleNameFound][ruleIndex].dependencies do
if pos(dependency, "%") <> 0 then
actualDependency := replace(dependency, "%", stemFound);
if fileType(convDosPath(actualDependency)) <> FILE_ABSENT or
actualDependency in makeData.rules then
ruleIndexFound := ruleIndex;
incr(numberOfRulesFound);
end if;
end if;
end for;
end for;
end if;
if numberOfRulesFound = 1 then
patternRule := makeData.patternRules[ruleNameFound][ruleIndexFound];
actualRule.target := targetName;
if stemFound = targetName then
for dependency range patternRule.dependencies do
if pos(dependency, "%") <> 0 then
actualDependency := replace(dependency, "%", stemFound);
if fileType(convDosPath(actualDependency)) <> FILE_ABSENT or
actualDependency in makeData.rules then
actualRule.dependencies &:= actualDependency;
end if;
else
actualRule.dependencies &:= dependency;
end if;
end for;
else
for dependency range patternRule.dependencies do
actualRule.dependencies &:= replace(dependency, "%", stemFound);
end for;
end if;
for dependency range dependencyRule.dependencies do
actualRule.dependencies &:= dependency;
end for;
actualRule.commands := patternRule.commands;
processRule(makeData, actualRule);
else
processRule(makeData, dependencyRule);
end if;
else
processRule(makeData, dependencyRule);
end if;
end func;
const proc: processRule (inout makeDataType: makeData, in string: targetName) is func
local
var string: ruleName is "";
var ruleType: emptyRule is ruleType.value;
begin
ruleName := applyMacros(makeData.macros, targetName, FALSE);
if ruleName <> "" then
if ruleName in makeData.rules then
if length(makeData.rules[ruleName].commands) <> 0 then
processRule(makeData, makeData.rules[ruleName]);
else
processPatternRule(makeData, ruleName, makeData.rules[ruleName]);
end if;
else
processPatternRule(makeData, ruleName, emptyRule);
end if;
end if;
end func;
const proc: make (in string: makefile, in array string: targets, in makeFlags: flags,
in stringHash: macros) is func
local
var ruleType: rule is ruleType.value;
var makeDataType: makeData is makeDataType.value;
var string: target is "";
begin
makeData.executeCommands := dontExecute not in flags;
makeData.inSilentMode := silentMode in flags;
makeData.doIgnoreErrors := ignoreErrors in flags;
makeData.macros := macros;
if "CPPFLAGS" not in makeData.macros then
makeData.macros @:= ["CPPFLAGS"] "";
end if;
if "CXXFLAGS" not in makeData.macros then
makeData.macros @:= ["CXXFLAGS"] "";
end if;
if "CFLAGS" not in makeData.macros then
makeData.macros @:= ["CFLAGS"] "";
end if;
if "CC" not in makeData.macros then
makeData.macros @:= ["CC"] "cc";
end if;
if "CXX" not in makeData.macros then
makeData.macros @:= ["CXX"] "c++";
end if;
makeData.macros @:= ["MAKE"] "make7";
if length(targets) <> 0 then
makeData.macros @:= ["MAKECMDGOALS"] join(targets, " ");
end if;
readMakefile(makeData, makefile);
if not patternRulePresent(makeData, "%.o", "%.c") then
if ".c.o" in makeData.rules then
rule.target := "%.o";
rule.dependencies := [] ("%.c");
rule.commands := makeData.rules[".c.o"].commands;
addPatternRule(makeData, rule);
excl(makeData.rules, ".c.o");
else
rule.target := "%.o";
rule.dependencies := [] ("%.c");
rule.commands := [] (applyMacros(makeData.macros, "$(CC) -c $(CPPFLAGS) $(CFLAGS) $< -o $@", TRUE));
addPatternRule(makeData, rule);
end if;
end if;
if not patternRulePresent(makeData, "%.obj", "%.c") then
if ".c.obj" in makeData.rules then
rule.target := "%.obj";
rule.dependencies := [] ("%.c");
rule.commands := makeData.rules[".c.obj"].commands;
addPatternRule(makeData, rule);
excl(makeData.rules, ".c.obj");
else
rule.target := "%.obj";
rule.dependencies := [] ("%.c");
rule.commands := [] (applyMacros(makeData.macros, "$(CC) -c $(CPPFLAGS) $(CFLAGS) $< -o $@", TRUE));
addPatternRule(makeData, rule);
end if;
end if;
if not patternRulePresent(makeData, "%.o", "%.cpp") then
if ".cpp.o" in makeData.rules then
rule.target := "%.o";
rule.dependencies := [] ("%.cpp");
rule.commands := makeData.rules[".cpp.o"].commands;
addPatternRule(makeData, rule);
excl(makeData.rules, ".cpp.o");
else
rule.target := "%.o";
rule.dependencies := [] ("%.cpp");
rule.commands := [] (applyMacros(makeData.macros, "$(CXX) -c $(CPPFLAGS) $(CXXFLAGS) $< -o $@", TRUE));
addPatternRule(makeData, rule);
end if;
end if;
if length(targets) = 0 then
processRule(makeData, makeData.targetOfFirstRule);
else
for target range targets do
processRule(makeData, target);
end for;
end if;
end func;
const proc: make (in string: makefile, in array string: targets, in makeFlags: flags) is func
begin
make(makefile, targets, flags, stringHash.value);
end func;
const proc: make (in string: makefile, in string: target, in makeFlags: flags, in stringHash: macros) is func
begin
if target = "" then
make(makefile, 0 times "", flags, macros);
else
make(makefile, [] (target), flags, macros);
end if;
end func;
const proc: make (in string: makefile, in string: target, in makeFlags: flags) is func
begin
make(makefile, target, flags, stringHash.value);
end func;