blob: 37ccb7ddd4d16ec6c6145f5d7526954020cd99d6 [file]
#!/usr/bin/env python3
# maketargetsrc creates target library target modules.
# Copyright (C) 2026 Free Software Foundation, Inc.
#
# This file is part of GNU Modula-2.
#
# GNU Modula-2 is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3, or (at your option)
# any later version.
#
# GNU Modula-2 is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with GCC; see the file COPYING3. If not see
# <http://www.gnu.org/licenses/>.
import argparse
import uuid
import os
import pathlib
import sys
exit_code = 0
minimalCommandLine = "-fno-scaffold-main -fno-scaffold-dynamic"
minimalCommandLine += " -fno-scaffold-static -fno-m2-plugin"
sourceLineLength = 60
def printf (format, *args):
# printf a very basic printf.
print(str(format) % args, end=' ')
def safeRemove(filename):
# Remove filename if the name is not None and the filename exists.
if (filename != None) and os.path.exists(filename):
os.remove(filename)
def quietSystem(args, commandLine, temporaryFile):
# Execute commandline and exit if unsuccessful. It will run
# gdb on the command line if --gdb is set in args.
if args.gdb:
result = os.system(commandLine + " -wrapper gdb,--args")
else:
result = os.system(commandLine + " 2>&1 /dev/null")
if result != 0:
safeRemove(temporaryFile)
safeRemove(args.outputfile)
printf("failed to execute: %s with an exit code: %d\n",
commandLine, result)
sys.exit(result)
def extractJoined():
# Return the commandLine and includePath.
commandLine = []
includePath = ""
defInput = ""
modInput = ""
compiler = ""
i = 1
while i<len(sys.argv):
if (len(sys.argv[i])>2) and (sys.argv[i][:3] == '-I='):
if includePath != "":
includePath += " "
includePath += "-I"
includePath += sys.argv[i][3:]
elif (len(sys.argv[i])>2) and (sys.argv[i][:3] == '-C='):
compiler = sys.argv[i][3:]
elif (len(sys.argv[i])>2) and (sys.argv[i][:3] == '-D='):
defInput = sys.argv[i][3:]
elif (len(sys.argv[i])>2) and (sys.argv[i][:3] == '-M='):
modInput = sys.argv[i][3:]
else:
commandLine += [sys.argv[i]]
i += 1
return commandLine, includePath, defInput, modInput, compiler
def initOptions():
# initOptions build and return the args object.
commandLine, includePath, defInput, modInput, compiler = extractJoined()
parser = argparse.ArgumentParser(prog='mktargetsrc')
parser.add_argument('-s', '--fiso', help='use the ISO dialect',
default=False, action='store_true')
parser.add_argument('-p', '--fpim', help='use the PIM dialect',
default=False, action='store_true')
parser.add_argument('-g', '--gdb', help='run gm2 under gdb',
default=False, action='store_true')
parser.add_argument('-o', '--outputfile', help='set the output file',
default=None, action='store')
parser.add_argument('-v', '--verbose', help='generate progress messages',
action='store_true')
parser.add_argument('-C=', '--compiler', help='the compiler driver',
default=compiler, action='store')
parser.add_argument('-D=', '--definputfile', help='set the def input file',
default=defInput, action='store')
parser.add_argument('-M=', '--modinputfile', help='set the mod input file',
default=modInput, action='store')
parser.add_argument('-I=', '--include', help='append to the search path',
default=includePath, action='store')
args = parser.parse_args(commandLine)
return args, includePath
def startWith(line, sub):
# Return True if line starts with sub.
return (len(line)>len(sub)) and (line[:len(sub)]==sub)
def makeDictionary(temporaryFile):
# Populate dictDefs with the contents of temporaryFile.
dictDefs = { "SYSTEM":{}, "builtin":{} }
for line in open(temporaryFile).readlines():
if startWith (line, "SYSTEM module creates type:"):
definedType = line[len("SYSTEM module creates type:"):]
definedType = definedType.strip()
dictDefs["SYSTEM"][definedType] = True
elif startWith (line, "builtin procedure function:"):
definedFunc = line[len("builtin procedure function:"):]
definedFunc = definedFunc.strip()
dictDefs["builtin"][definedFunc] = True
return dictDefs
def leadingSpaces(line):
# Return the leading number of spaces in line.
s = line.lstrip()
return len(line)-len(s)
def output_export_list(args, line, export_list):
# Append the export_list to the args.outputfile. It preserves
# indentation and wraps at sourceLineLength.
outf = open(args.outputfile, "a")
indent = leadingSpaces(line)
pos = 0
for el in export_list:
if pos == 0:
outf.write(" " * indent)
pos = indent
outf.write(el)
outf.write(", ")
pos += len(el)
pos += 2
if pos > sourceLineLength:
outf.write("\n")
pos = 0
if pos > 0:
outf.write("\n")
outf.close()
def output_SYSTEM_DATATYPES(args, line, dictDefs):
# Write a list of system datatypes into the output file.
output_export_list (args, line, [key for key in dictDefs["SYSTEM"]])
def output_SYSTEM_TYPES(args, line, dictDefs):
# Write a list of system types into the output file.
outf = open(args.outputfile, "a")
indent = leadingSpaces(line)
for key in dictDefs["SYSTEM"]:
outf.write(" " * indent)
outf.write(key)
outf.write(" ;\n")
outf.close()
def output(args, line):
# Append line to the output file.
outf = open(args.outputfile, "a")
outf.write(line)
outf.close()
def contains(line, sub):
# Return True if line contains sub.
return line.find(sub) >= 0
def contains_builtin_tag(line):
# Return True if line contains a builtin tag.
for key in builtin_func:
if contains(line, key):
return True
return False
class builtin_prototype:
def __init__(self, name, returnType, argNameList, argTypeList):
self.name = name
self.builtinName = "__builtin_" + name
self.returnType = returnType
self.argNameList = argNameList
self.argTypeList = argTypeList
# The list of gcc_builtins which might not be available on a target.
list_of_builtins = [builtin_prototype("csinl", "LONGCOMPLEX", ["z"], ["LONGCOMPLEX"]),
builtin_prototype("csin", "COMPLEX", ["z"], ["COMPLEX"]),
builtin_prototype("csinf", "SHORTCOMPLEX", ["z"], ["SHORTCOMPLEX"]),
builtin_prototype("ccosl", "LONGCOMPLEX", ["z"], ["LONGCOMPLEX"]),
builtin_prototype("ccos", "COMPLEX", ["z"], ["COMPLEX"]),
builtin_prototype("ccosf", "SHORTCOMPLEX", ["z"], ["SHORTCOMPLEX"]),
builtin_prototype("ctanl", "LONGCOMPLEX", ["z"], ["LONGCOMPLEX"]),
builtin_prototype("ctan", "COMPLEX", ["z"], ["COMPLEX"]),
builtin_prototype("ctanf", "SHORTCOMPLEX", ["z"], ["SHORTCOMPLEX"]),
builtin_prototype("casinf", "SHORTCOMPLEX", ["z"], ["SHORTCOMPLEX"]),
builtin_prototype("casin", "COMPLEX", ["z"], ["COMPLEX"]),
builtin_prototype("casinl", "LONGCOMPLEX", ["z"], ["LONGCOMPLEX"]),
builtin_prototype("cacosf", "SHORTCOMPLEX", ["z"], ["SHORTCOMPLEX"]),
builtin_prototype("cacos", "COMPLEX", ["z"], ["COMPLEX"]),
builtin_prototype("cacosl", "LONGCOMPLEX", ["z"], ["LONGCOMPLEX"]),
builtin_prototype("catanf", "SHORTCOMPLEX", ["z"], ["SHORTCOMPLEX"]),
builtin_prototype("catan", "COMPLEX", ["z"], ["COMPLEX"]),
builtin_prototype("catanl", "LONGCOMPLEX", ["z"], ["LONGCOMPLEX"])]
gcc_builtins = {element.builtinName: element for element in list_of_builtins}
def output_def_cbuiltin(args, keyname):
# Generate the definition module procedure declaration for a cbuiltin keyname.
output(args, "(* " + gcc_builtins[keyname].name + " is available on the target. *)\n")
output(args, "PROCEDURE %s (%s: %s) : %s ;\n" % (gcc_builtins[keyname].name,
gcc_builtins[keyname].argNameList[0],
gcc_builtins[keyname].argTypeList[0],
gcc_builtins[keyname].returnType))
def output_def_builtin(args, keyname):
# Generate the definition module procedure declaration for a builtin keyname.
output(args, "(* " + gcc_builtins[keyname].name + " is available on the target. *)\n")
output(args, "PROCEDURE __BUILTIN__ %s (%s: %s) : %s ;\n" % (gcc_builtins[keyname].name,
gcc_builtins[keyname].argNameList[0],
gcc_builtins[keyname].argTypeList[0],
gcc_builtins[keyname].returnType))
def process_def_builtin(args, line, key, dictDefs):
# Check target availability for the builtin and generate a procedure declaration
# or an unavailablity comment.
line = line.strip()
if startWith(line, "(*") and (len(line)>len("(*")):
line = line[2:]
funcname = line.split('(')[1].split(',')[0]
keyname = line.split('(')[1].split(',')[3]
if keyname in dictDefs["builtin"]:
output_def_builtin(args, keyname)
else:
unavailable(args, funcname)
def do_output_mod_builtin(args, keyname, line):
output(args, "(* " + gcc_builtins[keyname].name + " is available on the target. *)\n")
output(args, "PROCEDURE __ATTRIBUTE__ __BUILTIN__ ")
output(args, "((%s)) %s (%s: %s) : %s ;" % (gcc_builtins[keyname].builtinName,
gcc_builtins[keyname].name,
gcc_builtins[keyname].argNameList[0],
gcc_builtins[keyname].argTypeList[0],
gcc_builtins[keyname].returnType))
builtin = line.split('(')[1].split(',')[4].split(')')[0]
output(args, """
BEGIN
RETURN %s(z)
END %s ;
""" % (builtin, gcc_builtins[keyname].name))
def output_mod_builtin(args, keyname, line):
if keyname in gcc_builtins:
do_output_mod_builtin(args, keyname, line)
else:
unavailable(args, keyname)
def process_mod_builtin(args, line, key, dictDefs):
line = line.strip()
if startWith(line, "(*") and (len(line)>len("(*")):
line = line[2:]
funcname = line.split('(')[1].split(',')[0]
keyname = line.split('(')[1].split(',')[3]
if keyname in dictDefs["builtin"]:
output_mod_builtin(args, keyname, line)
else:
unavailable(args, funcname)
def unavailable(args, funcname):
output(args, "(* Procedure function %s is unavailable on the target. *)\n" % funcname)
def process_def_cbuiltin(args, line, key, dictDefs):
line = line.strip()
if startWith(line, "(*") and (len(line)>len("(*")):
line = line[2:]
funcname = line.split('(')[1].split(',')[0]
keyname = line.split('(')[1].split(',')[3]
output(args, "(* " + funcname + " is available on the target. *)\n")
if keyname in dictDefs["builtin"]:
output_def_cbuiltin(args, keyname)
else:
unavailable(args, funcname)
def target_builtins(args, key, dictDefs):
builtin_list = [gcc_builtins[key] for key in gcc_builtins]
target_list = []
for el in builtin_list:
if el.builtinName in dictDefs["builtin"]:
target_list += [el.name]
return target_list
def process_cbuiltin_export(args, line, key, dictDefs):
output_export_list (args, line, target_builtins (args, key, dictDefs))
def process_mod_target_procedure(args, line, key, dictDefs):
line = line.strip()
if startWith(line, "(*") and (len(line)>len("(*")):
line = line[2:]
funcname = line.split('(')[1].split(',')[0]
argtype = line.split('(')[1].split(',')[1]
returntype = line.split('(')[1].split(',')[2]
keyname = line.split('(')[1].split(',')[3]
cbuiltin = line.split('(')[1].split(',')[4].split('>')[0].split(')')[0]
if keyname in dictDefs["builtin"]:
output(args, """
PROCEDURE __ATTRIBUTE__ __BUILTIN__ ((%s)) %s (z: %s): %s ;
BEGIN
RETURN %s (z)
END %s ;
""" % (keyname, funcname, argtype, returntype, cbuiltin, funcname))
def process_def_target_procedure(args, line, key, dictDefs):
line = line.strip()
if startWith(line, "(*") and (len(line)>len("(*")):
line = line[2:]
funcname = line.split('(')[1].split(',')[0]
argtype = line.split('(')[1].split(',')[1]
returntype = line.split('(')[1].split(',')[2]
keyname = line.split('(')[1].split(',')[3]
if keyname in dictDefs["builtin"]:
output(args, "PROCEDURE %s (z: %s) : %s ;\n" % (funcname, argtype, returntype))
builtin_func = { "<DEF_CBUILTIN_PROCEDURE":process_def_cbuiltin,
"<DEF_BUILTIN_PROCEDURE":process_def_builtin,
"<MOD_BUILTIN_PROCEDURE":process_mod_builtin,
"<CBUILTIN_EXPORT_LIST>":process_cbuiltin_export,
"<DEF_TARGET_PROCEDURE":process_def_target_procedure,
"<MOD_TARGET_PROCEDURE":process_mod_target_procedure}
def process_tag(args, line, dictDefs):
for key in builtin_func:
if contains(line, key):
builtin_func[key](args, line, key, dictDefs)
def generateSource(args, temporaryFile):
# generateSource interpet and process any tags.
dictDefs = makeDictionary(temporaryFile)
seen_SYSTEM_DATATYPES = False
seen_SYSTEM_TYPES = False
filename = args.modinputfile
if args.definputfile != "":
filename = args.definputfile
if os.path.isfile(filename):
for line in open(filename):
if contains (line, "<SYSTEM_DATATYPES>"):
seen_SYSTEM_DATATYPES = True
elif contains (line, "</SYSTEM_DATATYPES>"):
output_SYSTEM_DATATYPES(args, line, dictDefs)
seen_SYSTEM_DATATYPES = False
elif contains (line, "<SYSTEM_TYPES>"):
seen_SYSTEM_TYPES = True
elif contains (line, "</SYSTEM_TYPES>"):
output_SYSTEM_TYPES(args, line, dictDefs)
seen_SYSTEM_TYPES = False
elif seen_SYSTEM_DATATYPES:
pass
elif contains_builtin_tag(line):
process_tag(args, line, dictDefs)
else:
output(args, line)
else:
printf("mktargetsrc cannot open: %s\n", filename)
sys.exit(1)
def makeTemporaryFile():
# makeTemporaryFile returns the name of a temporary file.
return str(uuid.uuid4())+'.txt'
def stripBackslash(compiler):
compiler = compiler.replace("\\ ", " ")
return compiler
def determineBuiltins(args, includePath):
# determineBuiltins build the builtin_dict with the gm2 builtins.
print(args.modinputfile)
if args.outputfile != None:
open(args.outputfile, "w").close()
temporaryFile = makeTemporaryFile()
dialect = '-fpim'
if args.fiso or (not args.fpim):
dialect = '-fiso'
compiler = stripBackslash(args.compiler)
commandLine = "%s %s %s %s -S -fdump-system-exports -o /dev/null %s > %s" % (
compiler, dialect, includePath, minimalCommandLine,
args.modinputfile, temporaryFile)
quietSystem(args, commandLine, temporaryFile)
commandLine = "%s %s %s %s -S -fdump-builtins %s >> %s" % (
compiler, dialect, includePath, minimalCommandLine,
args.modinputfile, temporaryFile)
quietSystem(args, commandLine, temporaryFile)
generateSource(args, temporaryFile)
os.remove(temporaryFile)
def main():
args, includePath = initOptions()
determineBuiltins(args, includePath)
main()