2017-04-25 14:19:50 +02:00
|
|
|
from __future__ import absolute_import, division, print_function
|
2017-02-24 10:26:56 +01:00
|
|
|
|
2016-02-10 13:19:29 +01:00
|
|
|
import os
|
|
|
|
|
import shutil
|
2016-04-12 13:08:38 +02:00
|
|
|
import subprocess
|
2017-04-11 12:28:57 +02:00
|
|
|
import sys
|
2016-02-10 13:19:29 +01:00
|
|
|
|
|
|
|
|
from langkit.compile_context import CompileCtx
|
2017-10-16 16:19:08 -04:00
|
|
|
from langkit.compiled_types import CompiledTypeMetaclass, create_builtin_types
|
2017-09-20 15:27:08 +02:00
|
|
|
from langkit.diagnostics import DiagnosticError, WarningSet
|
2017-06-12 15:49:13 +02:00
|
|
|
from langkit.dsl import _StructMetaclass, _ASTNodeMetaclass, _EnumNodeMetaclass
|
2017-09-27 11:44:56 +02:00
|
|
|
from langkit.expressions import Entity, Self
|
2016-04-12 13:08:38 +02:00
|
|
|
from langkit.libmanage import ManageScript
|
2017-02-01 15:04:11 +01:00
|
|
|
from langkit.utils import reset_memoized
|
2016-02-10 13:19:29 +01:00
|
|
|
|
|
|
|
|
|
2017-09-20 15:27:08 +02:00
|
|
|
default_warning_set = WarningSet()
|
|
|
|
|
|
2017-09-20 15:29:11 +02:00
|
|
|
# We don't want to be forced to provide dummy docs for public properties in
|
|
|
|
|
# testcases.
|
|
|
|
|
default_warning_set.disable(WarningSet.undocumented_public_properties)
|
|
|
|
|
|
2017-11-22 14:44:26 +01:00
|
|
|
pretty_print = bool(int(os.environ.get('LANGKIT_PRETTY_PRINT', '0')))
|
|
|
|
|
|
2017-11-15 09:50:25 +01:00
|
|
|
project_template = """
|
|
|
|
|
with "libfoolang";
|
|
|
|
|
|
|
|
|
|
project Gen is
|
|
|
|
|
for Languages use ("Ada");
|
|
|
|
|
for Source_Dirs use (".");
|
|
|
|
|
for Object_Dir use "obj";
|
|
|
|
|
for Main use ("{main_source}");
|
|
|
|
|
|
|
|
|
|
package Builder is
|
|
|
|
|
for Executable ("{main_source}") use "main";
|
|
|
|
|
end Builder;
|
|
|
|
|
|
|
|
|
|
package Compiler is
|
|
|
|
|
for Default_Switches ("Ada") use
|
|
|
|
|
("-g", "-O0", "-gnata", "-gnatwae", "-gnatyg");
|
|
|
|
|
end Compiler;
|
|
|
|
|
end Gen;
|
|
|
|
|
"""
|
|
|
|
|
|
2017-09-20 15:27:08 +02:00
|
|
|
|
|
|
|
|
def prepare_context(grammar, lexer=None, warning_set=default_warning_set):
|
2016-04-04 18:25:59 +02:00
|
|
|
"""
|
2016-04-12 13:08:38 +02:00
|
|
|
Create a compile context and prepare the build directory for code
|
|
|
|
|
generation.
|
2016-04-04 18:25:59 +02:00
|
|
|
|
2016-04-12 13:08:38 +02:00
|
|
|
:param langkit.parsers.Grammar grammar: The language grammar to use for
|
|
|
|
|
this context.
|
|
|
|
|
|
|
|
|
|
:param langkit.lexer.Lexer lexer: The language lexer to use for this
|
|
|
|
|
context.
|
2017-09-20 15:27:08 +02:00
|
|
|
|
|
|
|
|
:param WarningSet warning_set: Set of warnings to emit.
|
2016-04-04 18:25:59 +02:00
|
|
|
"""
|
2016-04-12 13:08:38 +02:00
|
|
|
|
2017-01-15 19:00:02 +01:00
|
|
|
if lexer is None:
|
|
|
|
|
from lexer_example import foo_lexer
|
|
|
|
|
lexer = foo_lexer
|
|
|
|
|
|
2016-02-10 13:19:29 +01:00
|
|
|
# Have a clean build directory
|
|
|
|
|
if os.path.exists('build'):
|
|
|
|
|
shutil.rmtree('build')
|
|
|
|
|
os.mkdir('build')
|
|
|
|
|
|
|
|
|
|
# Try to emit code
|
|
|
|
|
ctx = CompileCtx(lang_name='Foo',
|
|
|
|
|
lexer=lexer,
|
|
|
|
|
grammar=grammar)
|
2017-09-20 15:27:08 +02:00
|
|
|
ctx.warnings = warning_set
|
2017-11-22 14:44:26 +01:00
|
|
|
ctx.pretty_print = pretty_print
|
2016-04-12 13:08:38 +02:00
|
|
|
|
|
|
|
|
return ctx
|
|
|
|
|
|
|
|
|
|
|
2017-09-20 15:27:08 +02:00
|
|
|
def emit_and_print_errors(grammar, lexer=None,
|
|
|
|
|
warning_set=default_warning_set):
|
2016-04-12 13:08:38 +02:00
|
|
|
"""
|
|
|
|
|
Compile and emit code for CTX. Return whether this was successful.
|
|
|
|
|
|
2017-08-10 15:45:13 +02:00
|
|
|
:param langkit.parsers.Grammar grammar_fn: The language grammar to use.
|
2016-05-02 12:52:56 +02:00
|
|
|
|
|
|
|
|
:param langkit.lexer.Lexer lexer: The lexer to use along with the grammar.
|
2017-11-15 09:30:43 +01:00
|
|
|
Use `lexer_example.foo_lexer` if left to None.
|
2016-05-02 12:52:56 +02:00
|
|
|
|
2017-09-20 15:27:08 +02:00
|
|
|
:param WarningSet warning_set: Set of warnings to emit.
|
|
|
|
|
|
2016-04-12 13:08:38 +02:00
|
|
|
:rtype: bool
|
|
|
|
|
"""
|
2017-01-15 19:00:02 +01:00
|
|
|
|
|
|
|
|
if lexer is None:
|
|
|
|
|
from lexer_example import foo_lexer
|
|
|
|
|
lexer = foo_lexer
|
|
|
|
|
|
2016-02-10 13:19:29 +01:00
|
|
|
try:
|
2017-09-20 15:27:08 +02:00
|
|
|
ctx = prepare_context(grammar, lexer, warning_set)
|
2016-02-10 13:19:29 +01:00
|
|
|
ctx.emit('build', generate_lexer=False)
|
|
|
|
|
# ... and tell about how it went
|
2016-03-25 12:02:04 +01:00
|
|
|
except DiagnosticError:
|
|
|
|
|
# If there is a diagnostic error, don't say anything, the diagnostics
|
|
|
|
|
# are enough.
|
2016-04-04 18:25:59 +02:00
|
|
|
return False
|
2016-02-10 13:19:29 +01:00
|
|
|
else:
|
2017-02-24 10:26:56 +01:00
|
|
|
print('Code generation was successful')
|
2016-04-04 18:25:59 +02:00
|
|
|
return True
|
2016-05-02 12:52:56 +02:00
|
|
|
finally:
|
|
|
|
|
reset_langkit()
|
2016-03-16 18:06:40 +01:00
|
|
|
|
|
|
|
|
|
2017-10-13 13:49:19 -04:00
|
|
|
def build(grammar, lexer=None, warning_set=default_warning_set):
|
|
|
|
|
"""
|
|
|
|
|
Shortcut for `build_and_run` to only build.
|
|
|
|
|
"""
|
2017-11-15 09:50:25 +01:00
|
|
|
build_and_run(grammar, lexer=lexer, warning_set=warning_set)
|
2017-10-13 13:49:19 -04:00
|
|
|
|
|
|
|
|
|
2017-11-15 09:50:25 +01:00
|
|
|
def build_and_run(grammar, py_script=None, ada_main=None, lexer=None,
|
2018-01-16 16:02:57 +01:00
|
|
|
warning_set=default_warning_set, properties_logging=False,
|
|
|
|
|
generate_unparser=False):
|
2016-04-12 13:08:38 +02:00
|
|
|
"""
|
2017-10-13 13:49:19 -04:00
|
|
|
Compile and emit code for `ctx` and build the generated library. Then, if
|
2017-11-15 09:50:25 +01:00
|
|
|
`py_script` is not None, run it with this library available. If `ada_main`
|
|
|
|
|
is not None, build it and run it, too.
|
2016-04-12 13:08:38 +02:00
|
|
|
|
|
|
|
|
An exception is raised if any step fails (the script must return code 0).
|
2017-09-20 15:27:08 +02:00
|
|
|
|
2017-11-15 09:30:43 +01:00
|
|
|
:param langkit.lexer.Lexer lexer: The lexer to use along with the grammar.
|
|
|
|
|
See emit_and_print_errors.
|
2017-09-20 15:27:08 +02:00
|
|
|
:param WarningSet warning_set: Set of warnings to emit.
|
2017-11-15 09:30:43 +01:00
|
|
|
:param bool properties_logging: Whether to enable properties logging in
|
|
|
|
|
code generation.
|
2018-01-16 16:02:57 +01:00
|
|
|
:param bool generate_unparser: Whether to generate unparser.
|
2016-04-12 13:08:38 +02:00
|
|
|
"""
|
|
|
|
|
|
2017-01-15 19:00:02 +01:00
|
|
|
if lexer is None:
|
|
|
|
|
from lexer_example import foo_lexer
|
|
|
|
|
lexer = foo_lexer
|
|
|
|
|
|
2017-09-20 15:27:08 +02:00
|
|
|
ctx = prepare_context(grammar, lexer, warning_set)
|
2016-04-12 13:08:38 +02:00
|
|
|
|
|
|
|
|
class Manage(ManageScript):
|
|
|
|
|
def create_context(self, args):
|
|
|
|
|
return ctx
|
|
|
|
|
|
2017-11-23 17:22:41 +01:00
|
|
|
m = Manage(override_lang_source_dir=False)
|
2016-04-12 13:08:38 +02:00
|
|
|
|
2016-12-13 16:55:51 +01:00
|
|
|
extensions_dir = os.path.abspath('extensions')
|
|
|
|
|
if os.path.isdir(extensions_dir):
|
|
|
|
|
ctx.extensions_dir = extensions_dir
|
|
|
|
|
|
2017-04-11 12:28:57 +02:00
|
|
|
# First build the library. Forward all test.py's arguments to the libmanage
|
|
|
|
|
# call so that manual testcase runs can pass "-g", for instance.
|
|
|
|
|
argv = sys.argv[1:] + ['--full-error-traces', '-vnone', 'make']
|
2017-09-20 15:27:08 +02:00
|
|
|
for w in WarningSet.available_warnings:
|
|
|
|
|
argv.append('-{}{}'.format('W' if w in warning_set else 'w', w.name))
|
2017-10-19 16:35:07 -04:00
|
|
|
if properties_logging:
|
|
|
|
|
argv.append('--enable-properties-logging')
|
2017-11-22 14:44:26 +01:00
|
|
|
if pretty_print:
|
|
|
|
|
argv.append('--pretty-print')
|
2018-01-16 16:02:57 +01:00
|
|
|
if generate_unparser:
|
|
|
|
|
argv.append('--generate-unparser')
|
2016-04-12 13:08:38 +02:00
|
|
|
m.run(argv)
|
|
|
|
|
|
2017-11-22 12:51:32 +01:00
|
|
|
# Flush stdout and stderr, so that diagnostics appear deterministically
|
|
|
|
|
# before the script/program output.
|
|
|
|
|
sys.stdout.flush()
|
|
|
|
|
sys.stderr.flush()
|
|
|
|
|
|
2017-02-16 15:22:12 +01:00
|
|
|
# Write a "setenv" script to make developper investigation convenient
|
|
|
|
|
with open('setenv.sh', 'w') as f:
|
|
|
|
|
m.write_setenv(f)
|
|
|
|
|
|
2017-11-15 09:50:25 +01:00
|
|
|
env = m.derived_env()
|
|
|
|
|
|
|
|
|
|
def run(*argv):
|
|
|
|
|
subprocess.check_call(argv, env=env)
|
|
|
|
|
|
|
|
|
|
if py_script is not None:
|
|
|
|
|
# Run the Python script. Note that in order to use the generated
|
|
|
|
|
# library, we have to use the special Python interpreter the testsuite
|
|
|
|
|
# provides us. See the corresponding code in
|
|
|
|
|
# testuite_support/python_driver.py.
|
|
|
|
|
python_interpreter = os.environ['PYTHON_INTERPRETER']
|
|
|
|
|
run(python_interpreter, py_script)
|
|
|
|
|
|
|
|
|
|
if ada_main is not None:
|
|
|
|
|
# Generate a project file to build the given Ada main and then run the
|
|
|
|
|
# program.
|
|
|
|
|
with open('gen.gpr', 'w') as f:
|
|
|
|
|
f.write(project_template.format(main_source=ada_main))
|
|
|
|
|
run('gprbuild', '-Pgen', '-q', '-p',
|
|
|
|
|
'-XLIBRARY_TYPE=relocatable', '-XXMLADA_BUILD=relocatable')
|
|
|
|
|
run(os.path.join('obj', 'main'))
|
2016-04-12 13:08:38 +02:00
|
|
|
|
|
|
|
|
|
2016-03-16 18:06:40 +01:00
|
|
|
def reset_langkit():
|
|
|
|
|
"""
|
|
|
|
|
Reset global state in Langkit.
|
|
|
|
|
|
|
|
|
|
TODO: this is a hack to workaround another hack. At some point in the
|
|
|
|
|
future, we should get rid of this global state in Langkit.
|
|
|
|
|
"""
|
2017-06-14 13:34:01 +02:00
|
|
|
CompiledTypeMetaclass.root_grammar_class = None
|
|
|
|
|
CompiledTypeMetaclass.astnode_types = []
|
|
|
|
|
CompiledTypeMetaclass.struct_types = []
|
|
|
|
|
CompiledTypeMetaclass.env_metadata = None
|
|
|
|
|
CompiledTypeMetaclass.entity_info = None
|
2017-05-31 13:31:55 +02:00
|
|
|
Self.unfreeze()
|
2017-09-27 11:44:56 +02:00
|
|
|
Entity.unfreeze()
|
2016-11-16 16:07:59 +01:00
|
|
|
|
2017-10-16 16:19:08 -04:00
|
|
|
CompiledTypeMetaclass.type_dict.clear()
|
|
|
|
|
create_builtin_types()
|
2017-01-30 15:32:35 +01:00
|
|
|
|
2017-06-08 14:27:03 +02:00
|
|
|
_StructMetaclass.reset()
|
2017-06-12 15:49:13 +02:00
|
|
|
_ASTNodeMetaclass.reset()
|
|
|
|
|
_EnumNodeMetaclass.reset()
|
2017-06-08 14:27:03 +02:00
|
|
|
|
2017-01-30 15:32:35 +01:00
|
|
|
reset_memoized()
|