| # Licensed to the Apache Software Foundation (ASF) under one or more |
| # contributor license agreements. See the NOTICE file distributed with |
| # this work for additional information regarding copyright ownership. |
| # The ASF licenses this file to You under the Apache License, Version 2.0 |
| # (the "License"); you may not use this file except in compliance with |
| # the License. You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| |
| from distutils.core import setup, Extension |
| from distutils.command.build import build as _build |
| from distutils.command.clean import clean as _clean |
| from distutils.cmd import Command as _Command |
| from distutils.dep_util import newer_group |
| import distutils.ccompiler |
| import os |
| import glob |
| import platform |
| import re |
| import shutil |
| import subprocess |
| import sysconfig |
| |
| # Get a compiler object and and strings representing the compiler type and |
| # CFLAGS. |
| compiler = distutils.ccompiler.new_compiler() |
| cflags = sysconfig.get_config_var('CFLAGS') |
| compiler_type = distutils.ccompiler.get_default_compiler() |
| |
| # There's no public way to get a string representing the compiler executable |
| # out of distutils, but the member variable has been in the same place for a |
| # long time, so violating encapsulation may be ok. |
| compiler_name = " ".join(compiler.compiler) |
| |
| BASE_DIR = os.path.abspath(os.path.join(os.pardir, os.pardir, os.pardir)) |
| PARENT_DIR = os.path.abspath(os.pardir) |
| CFC_SOURCE_DIR = os.path.join(PARENT_DIR, 'src') |
| CFC_INCLUDE_DIR = os.path.join(PARENT_DIR, 'include') |
| COMMON_SOURCE_DIR = os.path.join(PARENT_DIR, 'common') |
| CHARMONIZER_C = os.path.join(COMMON_SOURCE_DIR, 'charmonizer.c') |
| CHARMONIZER_EXE_NAME = compiler.executable_filename('charmonizer') |
| CHARMONIZER_EXE_PATH = os.path.join(os.curdir, CHARMONIZER_EXE_NAME) |
| CHARMONY_H_PATH = 'charmony.h' |
| LEMON_DIR = os.path.join(BASE_DIR, 'lemon') |
| LEMON_EXE_NAME = compiler.executable_filename('lemon') |
| LEMON_EXE_PATH = os.path.join(LEMON_DIR, LEMON_EXE_NAME) |
| |
| # Accumulate lists of source files and target files. |
| c_filepaths = [] |
| y_filepaths = [] |
| paths_to_clean = [ |
| CHARMONIZER_EXE_PATH, |
| CHARMONY_H_PATH, |
| '_charm*', |
| ] |
| c_filepaths.append(os.path.join('clownfish', '_cfc.c')) |
| for (dirpath, dirnames, files) in os.walk(CFC_SOURCE_DIR): |
| for filename in files: |
| if filename.endswith('.y'): |
| path = os.path.join(dirpath, filename) |
| y_filepaths.append(path) |
| path = re.sub(r'y$', 'h', path) |
| paths_to_clean.append(path) |
| path = re.sub(r'h$', 'c', path) |
| paths_to_clean.append(path) |
| c_filepaths.append(path) |
| path = compiler.object_filenames([path])[0] |
| paths_to_clean.append(path) |
| if filename.endswith('.c'): |
| path = os.path.join(dirpath, filename) |
| c_filepaths.append(path) |
| path = compiler.object_filenames([path])[0] |
| paths_to_clean.append(path) |
| |
| def _quotify(text): |
| text = text.replace('\\', '\\\\') |
| text = text.replace('"', '\\"') |
| return '"' + text + '"' |
| |
| def _run_make(command=[], directory=None): |
| current_directory = os.getcwd(); |
| if (directory != None): |
| os.chdir(directory) |
| if (compiler_type == 'msvc'): |
| command.insert(0, 'Makefile.MSVC') |
| command.insert(0, '-f') |
| elif (platform.system() == 'Windows'): |
| command.insert(0, 'Makefile.MinGW') |
| command.insert(0, '-f') |
| command.insert(0, "make") |
| subprocess.check_call(command) |
| if (directory != None): |
| os.chdir(current_directory) |
| |
| class charmony(_Command): |
| description = "Build and run charmonizer" |
| user_options = [] |
| def initialize_options(self): |
| pass |
| def finalize_options(self): |
| pass |
| def run(self): |
| # Compile charmonizer. |
| if newer_group([CHARMONIZER_C], CHARMONIZER_EXE_PATH): |
| command = [compiler_name] |
| if compiler_type == 'msvc': |
| command.append('/Fe' + CHARMONIZER_EXE_PATH) |
| else: |
| command.extend(['-o', CHARMONIZER_EXE_PATH]) |
| command.append(CHARMONIZER_C) |
| print(" ".join(command)) |
| subprocess.check_call(command) |
| |
| # Run charmonizer. |
| if newer_group([CHARMONIZER_EXE_PATH], CHARMONY_H_PATH): |
| command = [ |
| CHARMONIZER_EXE_PATH, |
| '--cc=' + _quotify(compiler_name), |
| '--enable-c', |
| '--', |
| cflags |
| ] |
| if 'CHARM_VALGRIND' in os.environ: |
| command[0:0] = "valgrind", "--leak-check=yes"; |
| print(" ".join(command)) |
| subprocess.check_call(command) |
| |
| class lemon(_Command): |
| description = "Compile the Lemon parser generator" |
| user_options = [] |
| def initialize_options(self): |
| pass |
| def finalize_options(self): |
| pass |
| def run(self): |
| if not os.path.exists(LEMON_EXE_PATH): |
| _run_make(['CC=' + _quotify(compiler_name)], directory=LEMON_DIR) |
| |
| class parsers(_Command): |
| description = "Run .y files through lemon" |
| user_options = [] |
| def initialize_options(self): |
| pass |
| def finalize_options(self): |
| pass |
| def run(self): |
| for y_path in y_filepaths: |
| target = re.sub(r'y$', 'c', y_path) |
| if newer_group([y_path], target): |
| command = [LEMON_EXE_PATH, '-c', y_path] |
| subprocess.check_call(command) |
| |
| class my_clean(_clean): |
| def run(self): |
| _clean.run(self) |
| _run_make(command=['clean'], directory=LEMON_DIR) |
| for elem in paths_to_clean: |
| for path in glob.glob(elem): |
| print("removing " + path) |
| if os.path.isdir(path): |
| shutil.rmtree(path) |
| else: |
| os.unlink(path) |
| |
| class my_build(_build): |
| def run(self): |
| self.run_command('charmony') |
| self.run_command('lemon') |
| self.run_command('parsers') |
| _build.run(self) |
| |
| cfc_extension = Extension('clownfish._cfc', |
| define_macros = [('CFCPYTHON', None)], |
| include_dirs = [ |
| CFC_INCLUDE_DIR, |
| CFC_SOURCE_DIR, |
| os.curdir, |
| ], |
| sources = c_filepaths) |
| |
| setup(name = 'clownfish-cfc', |
| version = '0.4.1', |
| description = 'Clownfish compiler', |
| author = 'Apache Lucy Project', |
| author_email = 'dev at lucy dot apache dot org', |
| url = 'http://lucy.apache.org', |
| packages = ['clownfish.cfc', |
| ], |
| cmdclass = { |
| 'build': my_build, |
| 'clean': my_clean, |
| 'lemon': lemon, |
| 'charmony': charmony, |
| 'parsers': parsers, |
| }, |
| ext_modules = [cfc_extension]) |
| |