Skip to content

Commit

Permalink
_toolchain.cxx: new toolchain.
Browse files Browse the repository at this point in the history
  • Loading branch information
whitequark committed Aug 27, 2020
1 parent fde242a commit 4180cc5
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 0 deletions.
52 changes: 52 additions & 0 deletions nmigen/_toolchain/cxx.py
@@ -0,0 +1,52 @@
import tempfile
import sysconfig
import os.path
from distutils import ccompiler


__all__ = ["build_cxx"]


def build_cxx(*, cxx_sources, output_name, include_dirs, macros):
build_dir = tempfile.TemporaryDirectory(prefix="nmigen_cxx_")

cwd = os.getcwd()
try:
# Unforuntately, `ccompiler.compile` assumes the paths are relative, and interprets
# the directory name of the source path specially. That makes it necessary to build in
# the output directory directly.
os.chdir(build_dir.name)

cc_driver = ccompiler.new_compiler()
cc_driver.output_dir = "."

cc = sysconfig.get_config_var("CC")
cxx = sysconfig.get_config_var("CXX")
cflags = sysconfig.get_config_var("CCSHARED")
ld_ldflags = sysconfig.get_config_var("LDCXXSHARED")
cc_driver.set_executables(
compiler=f"{cc} {cflags}",
compiler_so=f"{cc} {cflags}",
compiler_cxx=f"{cxx} {cflags}",
linker_so=ld_ldflags,
)

for include_dir in include_dirs:
cc_driver.add_include_dir(include_dir)
for macro in macros:
cc_driver.define_macro(macro)
for cxx_filename, cxx_source in cxx_sources.items():
with open(cxx_filename, "w") as f:
f.write(cxx_source)

cxx_filenames = list(cxx_sources.keys())
obj_filenames = cc_driver.object_filenames(cxx_filenames)
so_filename = cc_driver.shared_object_filename(output_name)

cc_driver.compile(cxx_filenames)
cc_driver.link_shared_object(obj_filenames, output_filename=so_filename, target_lang="c++")

return build_dir, so_filename

finally:
os.chdir(cwd)
68 changes: 68 additions & 0 deletions tests/test_toolchain_cxx.py
@@ -0,0 +1,68 @@
import os
import ctypes
import tempfile
import unittest

from nmigen._toolchain.cxx import *


class ToolchainCxxTestCase(unittest.TestCase):
def setUp(self):
self.include_dir = None
self.build_dir = None

def tearDown(self):
if self.include_dir:
self.include_dir.cleanup()
if self.build_dir:
self.build_dir.cleanup()

def test_filename(self):
self.build_dir, filename = build_cxx(
cxx_sources={"test.cc": ""},
output_name="answer",
include_dirs=[],
macros=[],
)
self.assertTrue(filename.startswith("answer"))

def test_simple(self):
self.build_dir, filename = build_cxx(
cxx_sources={"test.cc": """
extern "C" int answer() { return 42; }
"""},
output_name="answer",
include_dirs=[],
macros=[],
)
library = ctypes.cdll.LoadLibrary(os.path.join(self.build_dir.name, filename))
self.assertEqual(library.answer(), 42)

def test_macro(self):
self.build_dir, filename = build_cxx(
cxx_sources={"test.cc": """
extern "C" int answer() { return ANSWER; }
"""},
output_name="answer",
include_dirs=[],
macros=["ANSWER=42"],
)
library = ctypes.cdll.LoadLibrary(os.path.join(self.build_dir.name, filename))
self.assertEqual(library.answer(), 42)

def test_include(self):
self.include_dir = tempfile.TemporaryDirectory(prefix="nmigen_hxx_")
with open(os.path.join(self.include_dir.name, "answer.h"), "w") as f:
f.write("#define ANSWER 42")

self.build_dir, filename = build_cxx(
cxx_sources={"test.cc": """
#include <answer.h>
extern "C" int answer() { return ANSWER; }
"""},
output_name="answer",
include_dirs=[self.include_dir.name],
macros=[],
)
library = ctypes.cdll.LoadLibrary(os.path.join(self.build_dir.name, filename))
self.assertEqual(library.answer(), 42)

0 comments on commit 4180cc5

Please sign in to comment.