mirror of
https://github.com/AxioDL/CodeGen.git
synced 2026-07-11 06:18:36 -07:00
Initial CMake Support + macOS support (not final)
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
.idea
|
||||
.dew
|
||||
dist
|
||||
__pycache__
|
||||
codegen.egg-info
|
||||
@@ -0,0 +1,80 @@
|
||||
cmake_minimum_required(VERSION 3.12)
|
||||
include(CMakePackageConfigHelpers)
|
||||
|
||||
project(codegen VERSION 0.1.0)
|
||||
|
||||
find_package(PythonInterp 3.6 REQUIRED)
|
||||
|
||||
add_library(codegen INTERFACE)
|
||||
target_compile_definitions(
|
||||
codegen
|
||||
INTERFACE
|
||||
WITH_CODEGEN
|
||||
)
|
||||
|
||||
add_custom_target(
|
||||
codegen_sdist
|
||||
ALL
|
||||
COMMAND
|
||||
"${PYTHON_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/setup.py" "sdist" "-d" "${PROJECT_BINARY_DIR}/dist"
|
||||
WORKING_DIRECTORY
|
||||
"${PROJECT_SOURCE_DIR}"
|
||||
)
|
||||
add_dependencies(codegen codegen_sdist)
|
||||
|
||||
# Install the CMake Module
|
||||
install(
|
||||
DIRECTORY "${PROJECT_SOURCE_DIR}/cmake"
|
||||
DESTINATION "share"
|
||||
)
|
||||
|
||||
# Install the python package
|
||||
install(
|
||||
FILES "${PROJECT_BINARY_DIR}/dist/codegen-${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}.tar.gz"
|
||||
DESTINATION "share/codegen/"
|
||||
)
|
||||
|
||||
# Install the project headers
|
||||
install(
|
||||
DIRECTORY "${PROJECT_SOURCE_DIR}/include/codegen"
|
||||
DESTINATION "include/codegen"
|
||||
)
|
||||
|
||||
|
||||
set(version_config_file "${PROJECT_BINARY_DIR}/codegenConfigVersion.cmake")
|
||||
set(config_file "${PROJECT_BINARY_DIR}/codegenConfig.cmake")
|
||||
set(config_install_dir "lib/cmake/codegen")
|
||||
|
||||
|
||||
# Associate target with export
|
||||
install(
|
||||
TARGETS codegen
|
||||
EXPORT codegenTargets
|
||||
INCLUDES DESTINATION "include/codegen"
|
||||
)
|
||||
|
||||
# Install the target config files
|
||||
install(
|
||||
EXPORT codegenTargets
|
||||
NAMESPACE "codegen::"
|
||||
DESTINATION "${config_install_dir}"
|
||||
)
|
||||
|
||||
# Generate version config file
|
||||
write_basic_package_version_file(
|
||||
"${version_config_file}"
|
||||
COMPATIBILITY SameMajorVersion
|
||||
)
|
||||
|
||||
# Generate config file
|
||||
configure_package_config_file(
|
||||
"Config.cmake.in"
|
||||
"${config_file}"
|
||||
INSTALL_DESTINATION "lib/cmake/codegen"
|
||||
)
|
||||
|
||||
# Install the config files
|
||||
install(
|
||||
FILES "${config_file}" "${version_config_file}"
|
||||
DESTINATION ${config_install_dir}
|
||||
)
|
||||
@@ -0,0 +1,4 @@
|
||||
@PACKAGE_INIT@
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/codegenTargets.cmake")
|
||||
check_required_components(codegen)
|
||||
@@ -0,0 +1,217 @@
|
||||
cmake_minimum_required(VERSION 3.12)
|
||||
|
||||
function(add_codegen_targets
|
||||
source_files
|
||||
generated_files_var
|
||||
input_root
|
||||
output_root
|
||||
include_directories
|
||||
)
|
||||
#
|
||||
# Find our python interpreter, and set up some python related variables.
|
||||
#
|
||||
find_package(PythonInterp 3.6 REQUIRED)
|
||||
set(venv_path "${CMAKE_CURRENT_BINARY_DIR}/codegen_venv")
|
||||
set(venv_dummy "${CMAKE_CURRENT_BINARY_DIR}/codegen_venv_dummy")
|
||||
set(package_dummy "${CMAKE_CURRENT_BINARY_DIR}/codegen_package_dummy")
|
||||
|
||||
set(missing_requirements FALSE)
|
||||
|
||||
#
|
||||
# Find libclang.
|
||||
#
|
||||
find_library(CLANG_LIBRARY NAMES clang libclang REQUIRED)
|
||||
if ("${CLANG_LIBRARY}" STREQUAL "CLANG_LIBRARY-NOTFOUND")
|
||||
message(SEND_ERROR "libclang not found")
|
||||
set(missing_requirements TRUE)
|
||||
endif()
|
||||
|
||||
#
|
||||
# Find the codegen python package file
|
||||
#
|
||||
if ("${CODEGEN_PACKAGE}" STREQUAL "")
|
||||
foreach (prefix ${CMAKE_PREFIX_PATH})
|
||||
set(codegen_package_candidate "${prefix}/share/codegen/codegen-0.1.0.tar.gz")
|
||||
if (EXISTS "${codegen_package_candidate}")
|
||||
set(CODEGEN_PACKAGE "${codegen_package_candidate}")
|
||||
break()
|
||||
endif()
|
||||
endforeach()
|
||||
if ("${CODEGEN_PACKAGE}" STREQUAL "")
|
||||
set(CODEGEN_PACKAGE "CODEGEN_PACKAGE-NOTFOUND" CACHE FILEPATH)
|
||||
endif()
|
||||
endif()
|
||||
if ("${CODEGEN_PACKAGE}" STREQUAL "CODEGEN_PACKAGE-NOTFOUND")
|
||||
message(SEND_ERROR "codegen package not found")
|
||||
set(missing_requirements TRUE)
|
||||
endif()
|
||||
|
||||
if (missing_requirements)
|
||||
message(FATAL_ERROR "Missing requirements")
|
||||
endif()
|
||||
|
||||
#
|
||||
# Get name of python executable. We gotta add a '.exe' prefix if we're on windows, obviously.
|
||||
#
|
||||
if (WIN32)
|
||||
set(sep ";")
|
||||
set(python_executable_name "python.exe")
|
||||
else()
|
||||
set(sep ":")
|
||||
set(python_executable_name "python")
|
||||
endif()
|
||||
|
||||
#
|
||||
# Determine the path of the virtual env python wrapper. It's different on Windows.
|
||||
#
|
||||
set(venv_python_executable_path "bin/${python_executable_name}")
|
||||
if (NOT EXISTS "${venv_python_executable_path}" AND WIN32)
|
||||
set(venv_python_executable_path "Scripts/${python_executable_name}")
|
||||
endif()
|
||||
|
||||
#
|
||||
# Setup the virtual env for the codegen tool to use if it hasn't been set up.
|
||||
#
|
||||
if (NOT EXISTS "${venv_dummy}")
|
||||
message(STATUS "Creating virtual env at ${venv_path}")
|
||||
execute_process(
|
||||
COMMAND "${PYTHON_EXECUTABLE}" -m ensurepip
|
||||
RESULT_VARIABLE update_venv_result
|
||||
OUTPUT_VARIABLE update_venv_output
|
||||
ERROR_VARIABLE update_venv_error
|
||||
)
|
||||
if (NOT update_venv_result EQUAL 0)
|
||||
# ensurepip module failed not installed. Check if pip is installed, and cause an error if it's not installed.
|
||||
execute_process(
|
||||
COMMAND "${PYTHON_EXECUTABLE}" -m pip --version
|
||||
RESULT_VARIABLE pip_version_result
|
||||
)
|
||||
if (NOT pip_version_result EQUAL 0)
|
||||
message(FATAL_ERROR "Failed to run ensurepip module, and pip is not installed. Please install pip manually.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
execute_process(
|
||||
COMMAND "${PYTHON_EXECUTABLE}" -m pip install virtualenv
|
||||
RESULT_VARIABLE update_venv_result
|
||||
OUTPUT_VARIABLE update_venv_output
|
||||
ERROR_VARIABLE update_venv_error
|
||||
)
|
||||
if (NOT update_venv_result EQUAL 0)
|
||||
message(FATAL_ERROR "Failed to install virtualenv with pip: result: ${update_venv_result}. stderr:\n${update_venv_error}")
|
||||
endif()
|
||||
|
||||
execute_process(
|
||||
COMMAND "${PYTHON_EXECUTABLE}" -m virtualenv "${venv_path}"
|
||||
RESULT_VARIABLE update_venv_result
|
||||
OUTPUT_VARIABLE update_venv_output
|
||||
ERROR_VARIABLE update_venv_error
|
||||
)
|
||||
if (NOT update_venv_result EQUAL 0)
|
||||
message(FATAL_ERROR "Cannot update codegen tool venv: result: ${update_venv_result}. stderr:\n${update_venv_error}")
|
||||
endif()
|
||||
|
||||
#
|
||||
# Install the codegen package with pip
|
||||
#
|
||||
execute_process(
|
||||
COMMAND
|
||||
"${venv_path}/${venv_python_executable_path}" "-m" "pip"
|
||||
"install" "${CODEGEN_PACKAGE}"
|
||||
RESULT_VARIABLE pip_result
|
||||
ERROR_VARIABLE pip_error
|
||||
)
|
||||
if (NOT pip_result EQUAL 0)
|
||||
message(FATAL_ERROR "Failed to install codegen packages into codegen virtualenv. result: ${pip_result}. stderr:\n${pip_error}")
|
||||
endif()
|
||||
file(TOUCH "${package_dummy}")
|
||||
|
||||
#
|
||||
# Create the dummy file to signify that we have successfully created the venv.
|
||||
#
|
||||
file(TOUCH "${venv_dummy}")
|
||||
endif()
|
||||
|
||||
#
|
||||
# Add build-time target to update the codegen package if it's touched.
|
||||
#
|
||||
add_custom_command(
|
||||
OUTPUT
|
||||
"${package_dummy}"
|
||||
COMMAND
|
||||
"${venv_path}/${venv_python_executable_path}" "-m" "pip"
|
||||
"install" "${CODEGEN_PACKAGE}"
|
||||
COMMAND
|
||||
"${CMAKE_COMMAND}" "-E" "touch" "${package_dummy}"
|
||||
DEPENDS
|
||||
"${CODEGEN_PACKAGE}"
|
||||
)
|
||||
|
||||
set(include_directories_arguments "")
|
||||
foreach(include_directory include_directories)
|
||||
list(APPEND include_directories_arguments "-I")
|
||||
list(APPEND include_directories_arguments "${include_directory}")
|
||||
endforeach()
|
||||
|
||||
message(STATUS "Updating codegen targets")
|
||||
|
||||
#
|
||||
# Set up targets for all files which will be generated by the codegen tool.
|
||||
#
|
||||
set(all_output_files "")
|
||||
foreach(current_source_file ${source_files})
|
||||
#
|
||||
# Determine which files are generated by this source file at configure time
|
||||
#
|
||||
execute_process(
|
||||
COMMAND
|
||||
"${venv_path}/${venv_python_executable_path}" "-m" "codegen"
|
||||
"get_output_files"
|
||||
"${current_source_file}"
|
||||
${include_directories_arguments}
|
||||
"--libclangpath" "${CLANG_LIBRARY}"
|
||||
"--source-root" "${input_root}"
|
||||
"--output-root" "${output_root}"
|
||||
OUTPUT_VARIABLE current_output_files
|
||||
ERROR_VARIABLE tool_error
|
||||
RESULT_VARIABLE tool_result
|
||||
)
|
||||
if (NOT tool_result EQUAL 0)
|
||||
message(SEND_ERROR "Error running codegen tool. result: ${tool_result}. stderr:\n${tool_error}")
|
||||
continue()
|
||||
endif()
|
||||
|
||||
#
|
||||
# Set up a build target for the outputs given to us by the above commands.
|
||||
#
|
||||
string(STRIP "${current_output_files}" current_output_files)
|
||||
list(LENGTH current_output_files current_output_files_len)
|
||||
if ("${current_output_files_len}" EQUAL 0)
|
||||
continue()
|
||||
endif()
|
||||
|
||||
foreach(current_output_file ${current_output_files})
|
||||
list(APPEND all_output_files "${current_output_file}")
|
||||
endforeach()
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT "${current_output_files}"
|
||||
COMMAND
|
||||
"${venv_path}/${venv_python_executable_path}" "-m" "codegen"
|
||||
"generate"
|
||||
"${current_source_file}"
|
||||
${include_directories_arguments}
|
||||
"--libclangpath" "${CLANG_LIBRARY}"
|
||||
"--source-root" "${input_root}"
|
||||
"--output-root" "${output_root}"
|
||||
DEPENDS "${current_source_file}" "${package_dummy}"
|
||||
)
|
||||
|
||||
endforeach()
|
||||
|
||||
#
|
||||
# "Return" a list of all files that will be generated during build time.
|
||||
#
|
||||
set("${generated_files_var}" "${all_output_files}" PARENT_SCOPE)
|
||||
|
||||
endfunction()
|
||||
@@ -0,0 +1,3 @@
|
||||
import codegen.cli
|
||||
|
||||
exit(codegen.cli.main())
|
||||
@@ -0,0 +1,50 @@
|
||||
import sys
|
||||
import argparse
|
||||
from typing import List
|
||||
|
||||
from codegen import codegen
|
||||
|
||||
|
||||
class ArgumentData(object):
|
||||
def __init__(self) -> None:
|
||||
self.command = ''
|
||||
self.source_file = ''
|
||||
self.libclangpath = ''
|
||||
self.include_paths: List[str] = []
|
||||
self.source_root = ''
|
||||
self.output_root = ''
|
||||
|
||||
|
||||
argparser = argparse.ArgumentParser()
|
||||
argparser.add_argument('command', help='What to do')
|
||||
argparser.add_argument('source_file', help='Source file to use')
|
||||
argparser.add_argument('--libclangpath', help='Path to libclang library file', required=True)
|
||||
argparser.add_argument('--include', '-I', help='Define an include path', dest='include_paths', action='append')
|
||||
argparser.add_argument('--source-root', help='Root path of all source files', required=True)
|
||||
argparser.add_argument('--output-root', help='Root path of all files to be output', required=True)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
args = ArgumentData()
|
||||
|
||||
# noinspection PyTypeChecker
|
||||
argparser.parse_args(namespace=args)
|
||||
|
||||
if args.command.lower() == 'get_output_files':
|
||||
return do_get_output_files_command(args)
|
||||
if args.command.lower() == 'generate':
|
||||
return do_generate_command(args)
|
||||
|
||||
sys.stderr.write('Invalid Command\n')
|
||||
return 1
|
||||
|
||||
|
||||
def do_get_output_files_command(args: ArgumentData) -> int:
|
||||
output_files = codegen.get_output_files(args.source_file, args.include_paths, args.libclangpath, args.source_root, args.output_root)
|
||||
sys.stdout.write(';'.join(output_files))
|
||||
return 0
|
||||
|
||||
|
||||
def do_generate_command(args: ArgumentData) -> int:
|
||||
codegen.RunCodegen(args.source_file, args.include_paths, args.libclangpath, args.source_root, args.output_root)
|
||||
return 0
|
||||
+48
-127
@@ -1,13 +1,13 @@
|
||||
# C++ code generation using clang
|
||||
import clang.cindex
|
||||
import cProfile
|
||||
import glob
|
||||
import mako.template
|
||||
import os
|
||||
import sys
|
||||
|
||||
from typing import List, Iterable
|
||||
|
||||
# Directory that the codegen script file is stored in
|
||||
ScriptDir = os.path.dirname(__file__)
|
||||
TemplateDir = os.path.normpath(os.path.join(os.path.dirname(__file__), 'data'))
|
||||
|
||||
# [debug] Whether to enable profiling
|
||||
EnableProfiling = False
|
||||
@@ -16,10 +16,10 @@ EnableProfiling = False
|
||||
PrintAST = False
|
||||
|
||||
# [debug] Path to store auto-generated codegen source files
|
||||
ExportPath = "build\\codegen"
|
||||
ExportPath = "build/codegen"
|
||||
|
||||
# [debug] Path to store the final output cpp file
|
||||
OutputCppSource = "%s\\auto_codegen.cpp" % ExportPath
|
||||
OutputCppSource = "%s/auto_codegen.cpp" % ExportPath
|
||||
|
||||
# [debug] Path to find the source files
|
||||
SourceRoot = "."
|
||||
@@ -33,6 +33,7 @@ InputViaCommandLine = True
|
||||
# Whether to always do a full regen
|
||||
ForceFullRegen = False
|
||||
|
||||
|
||||
def GetCursorFullyQualifiedName(Cursor):
|
||||
""" Return the fully qualified name of the object represented by Cursor, including any parent scopes """
|
||||
OutName = Cursor.spelling
|
||||
@@ -150,7 +151,7 @@ class ScopedDeclare:
|
||||
|
||||
def DebugPrint(self):
|
||||
print( self.GenerateText(0) )
|
||||
|
||||
|
||||
class SourceFile:
|
||||
""" A C++ source code file """
|
||||
def __init__(self, FilePath):
|
||||
@@ -158,17 +159,17 @@ class SourceFile:
|
||||
self.Enums = []
|
||||
self.RootDeclare = ScopedDeclare("", "")
|
||||
|
||||
def GetCodegenFile(self):
|
||||
def GetCodegenFile(self, SourceRoot: str, OutputRoot: str):
|
||||
""" Returns the path to store the auto-generated code for this source file """
|
||||
if os.path.isabs(self.FilePath):
|
||||
RelativePath = self.FilePath[3:]
|
||||
return "%s\\%s.codegen.inl" % (ExportPath, RelativePath)
|
||||
else:
|
||||
return "%s\\%s.codegen.inl" % (ExportPath, self.FilePath)
|
||||
abspath = os.path.abspath(self.FilePath)
|
||||
relpath = os.path.relpath(abspath, SourceRoot)
|
||||
reldir, filename = os.path.split(relpath)
|
||||
filename_no_ext, _ = os.path.splitext(filename)
|
||||
return os.path.join(OutputRoot, reldir, filename_no_ext + '_codegen.cpp')
|
||||
|
||||
def LastModifiedTime(self):
|
||||
""" Returns the last modified time for this source file """
|
||||
return os.path.getmtime( self.FilePath )
|
||||
return os.path.getmtime(self.FilePath)
|
||||
|
||||
def Analyze(self, ClangIndex, CompileEnvironment):
|
||||
""" Analyzes the AST for any components that need generated code """
|
||||
@@ -179,23 +180,21 @@ class SourceFile:
|
||||
if PrintAST:
|
||||
DebugPrintCursorRecursive(TranslationUnit.cursor, self.FilePath)
|
||||
print("")
|
||||
|
||||
# Generate code
|
||||
|
||||
def Generate(self, OutputPath: str):
|
||||
if self.Enums:
|
||||
MakoTemplateFile = open("%s/template.mako" % ScriptDir, "r")
|
||||
MakoTemplateText = MakoTemplateFile.read()
|
||||
MakoTemplateFile.close()
|
||||
|
||||
with open("%s/template.mako" % TemplateDir, "r") as MakoTemplateFile:
|
||||
MakoTemplateText = MakoTemplateFile.read()
|
||||
|
||||
MakoTemplate = mako.template.Template(MakoTemplateText)
|
||||
GeneratedCode = MakoTemplate.render(Enums=self.Enums, IncludeFile=self.FilePath, ForwardDeclares=self.RootDeclare.Children)
|
||||
|
||||
OutPath = self.GetCodegenFile()
|
||||
OutDir = os.path.dirname(OutPath)
|
||||
GeneratedCode = MakoTemplate.render(Enums=self.Enums, IncludeFile=self.FilePath,
|
||||
ForwardDeclares=self.RootDeclare.Children)
|
||||
|
||||
OutDir = os.path.dirname(OutputPath)
|
||||
os.makedirs(OutDir, exist_ok=True)
|
||||
|
||||
OutCodegenFile = open(OutPath, "w")
|
||||
OutCodegenFile.write(GeneratedCode)
|
||||
OutCodegenFile.close()
|
||||
|
||||
with open(OutputPath, "w") as file:
|
||||
file.write(GeneratedCode)
|
||||
|
||||
def CursorRecurse(self, Cursor, Depth):
|
||||
""" Recursive function that performs the actual analysis work of the AST """
|
||||
@@ -248,114 +247,36 @@ class SourceFile:
|
||||
else:
|
||||
self.CursorRecurse(Child, Depth + 1)
|
||||
|
||||
def RunCodegen():
|
||||
# Source files to parse
|
||||
SourceFiles = []
|
||||
IncludePaths = []
|
||||
OutCodegenSource = ""
|
||||
|
||||
if ParseCmdLine:
|
||||
global InputViaCommandLine
|
||||
|
||||
for i in range(0, len(sys.argv)):
|
||||
arg = sys.argv[i]
|
||||
|
||||
if InputViaCommandLine is True and arg == "-sourcefiles" or arg == "-include":
|
||||
i += 1
|
||||
|
||||
while i < len(sys.argv) and sys.argv[i][0] != '-':
|
||||
File = sys.argv[i]
|
||||
|
||||
if arg == "-sourcefiles":
|
||||
# this isn't the best way to handle this since the output file is provided via commandline arg
|
||||
if "auto_codegen" not in File:
|
||||
SourceFiles.append(File)
|
||||
else:
|
||||
IncludePaths.append(File)
|
||||
|
||||
i += 1
|
||||
|
||||
i -= 1
|
||||
|
||||
# The only time we don't use commandline input is for debugging
|
||||
if arg == "-cmdinput":
|
||||
InputViaCommandLine = True
|
||||
|
||||
if arg == "-full":
|
||||
global ForceFullRegen
|
||||
ForceFullRegen = True
|
||||
|
||||
if arg == "-o":
|
||||
i += 1
|
||||
OutCodegenSource = sys.argv[i]
|
||||
|
||||
global ExportPath
|
||||
ExportPath = os.path.dirname(OutCodegenSource)
|
||||
|
||||
if arg == "-pwd":
|
||||
i += 1
|
||||
WorkingDirectory = sys.argv[i]
|
||||
os.chdir(WorkingDirectory)
|
||||
|
||||
if not InputViaCommandLine:
|
||||
SourceFiles = glob.glob("%s\\**\\*.cpp" % SourceRoot, recursive=True)
|
||||
SourceFiles += glob.glob("%s\\**\\*.hpp" % SourceRoot, recursive=True)
|
||||
SourceFiles += glob.glob("%s\\**\\*.h" % SourceRoot, recursive=True)
|
||||
OutCodegenSource = OutputCppSource
|
||||
|
||||
print("Codegen Input: " + " ".join(SourceFiles))
|
||||
print("Build Dir: " + ExportPath)
|
||||
|
||||
def RunCodegen(file_path: str, include_paths: Iterable[str], lib_clang_path: str, source_root: str, output_root: str):
|
||||
file = GetAnalyzedSourceFile(file_path, include_paths, lib_clang_path)
|
||||
output_path = file.GetCodegenFile(source_root, output_root)
|
||||
file.Generate(output_path)
|
||||
|
||||
|
||||
# if EnableProfiling:
|
||||
# cProfile.run('RunCodegen()')
|
||||
# else:
|
||||
# RunCodegen()
|
||||
|
||||
|
||||
def GetAnalyzedSourceFile(FilePath: str, IncludePaths: Iterable[str], LibClangPath: str) -> SourceFile:
|
||||
# Clang index
|
||||
#@todo - this definitely isn't portable??? how should I be setting this???
|
||||
clang.cindex.Config.set_library_file('C:\\Program Files\\LLVM\\bin\\libclang.dll')
|
||||
clang.cindex.Config.set_library_file(LibClangPath)
|
||||
ClangIndex = clang.cindex.Index.create()
|
||||
|
||||
# C++ environment
|
||||
CompileEnvironment = CxxCompileEnvironment(IncludePaths)
|
||||
|
||||
# Process all source files
|
||||
LastRegenTime = os.path.getmtime(OutCodegenSource) if os.path.isfile(OutCodegenSource) else 0
|
||||
OutFiles = []
|
||||
NewFile = SourceFile(FilePath)
|
||||
NewFile.Analyze(ClangIndex, CompileEnvironment)
|
||||
return NewFile
|
||||
|
||||
for FilePath in SourceFiles:
|
||||
# If the file doesn't exist, just skip it.
|
||||
# This mirrors qmake behavior.
|
||||
if not os.path.isfile(FilePath):
|
||||
continue
|
||||
|
||||
NewFile = SourceFile(FilePath)
|
||||
NewFileOut = NewFile.GetCodegenFile()
|
||||
|
||||
# Only run codegen on this file if it has been modified since last time
|
||||
if ForceFullRegen or NewFile.LastModifiedTime() > LastRegenTime:
|
||||
NewFile.Analyze(ClangIndex, CompileEnvironment)
|
||||
|
||||
if NewFile.Enums:
|
||||
# Register this as a file that needs to be included in the output source
|
||||
OutFiles.append(NewFileOut)
|
||||
else:
|
||||
# Delete codegen file if it exists
|
||||
if os.path.isfile(NewFileOut):
|
||||
os.remove(NewFileOut)
|
||||
|
||||
# If the file hasn't been modified, we still want to make sure it gets included in the output source
|
||||
else:
|
||||
if os.path.isfile(NewFileOut):
|
||||
OutFiles.append(NewFileOut)
|
||||
|
||||
# Create final output cpp
|
||||
OutDir = os.path.dirname(OutCodegenSource)
|
||||
os.makedirs(OutDir, exist_ok=True)
|
||||
def get_output_files(source_file: str, include_paths: Iterable[str], lib_clang_path: str, source_root: str, output_root: str) -> List[str]:
|
||||
file = GetAnalyzedSourceFile(source_file, include_paths, lib_clang_path)
|
||||
|
||||
OutFile = open(OutCodegenSource, "w")
|
||||
OutFile.write("#pragma warning( push )\n")
|
||||
OutFile.write("#pragma warning( disable : 4146 )\n") # Suppress C4146: unary minus operator applied to unsigned type, result still unsgined
|
||||
[OutFile.write("#include \"%s\"\n" % os.path.normpath(CodegenFile)) for CodegenFile in OutFiles]
|
||||
OutFile.write("#pragma warning( pop )\n")
|
||||
OutFile.close()
|
||||
if not file.Enums:
|
||||
return []
|
||||
|
||||
if EnableProfiling:
|
||||
cProfile.run('RunCodegen()')
|
||||
else:
|
||||
RunCodegen()
|
||||
return [file.GetCodegenFile(source_root, output_root)]
|
||||
@@ -0,0 +1,30 @@
|
||||
% if Enums:
|
||||
#include <codegen/EnumReflection.h>
|
||||
% endif
|
||||
#include "${IncludeFile}"
|
||||
|
||||
#pragma warning( push )
|
||||
#pragma warning( disable : 4146 ) // Suppress C4146: unary minus operator applied to unsigned type, result still unsgined
|
||||
|
||||
## % for Decl in ForwardDeclares:
|
||||
## ${Decl.GenerateText(0)}
|
||||
## % endfor
|
||||
|
||||
% for Enum in Enums:
|
||||
template <>
|
||||
const CEnumNameMap TEnumReflection<${Enum.FullName}>::skNameMap = {
|
||||
<% ValueSet = set() %> \
|
||||
% for Constant in Enum.Constants:
|
||||
% if Constant.Value not in ValueSet:
|
||||
{ ${Constant.Value}, "${Constant.Name}" },
|
||||
<% ValueSet.add(Constant.Value) %> \
|
||||
% endif
|
||||
% endfor
|
||||
};
|
||||
|
||||
template <>
|
||||
const int TEnumReflection<${Enum.FullName}>::skErrorValue = ${Enum.ErrorValue};
|
||||
|
||||
% endfor
|
||||
|
||||
#pragma warning( pop )
|
||||
@@ -0,0 +1,201 @@
|
||||
"""A setuptools based setup module.
|
||||
See:
|
||||
https://packaging.python.org/en/latest/distributing.html
|
||||
https://github.com/pypa/sampleproject
|
||||
"""
|
||||
|
||||
# Always prefer setuptools over distutils
|
||||
from setuptools import setup, find_packages
|
||||
from os import path
|
||||
# io.open is needed for projects that support Python 2.7
|
||||
# It ensures open() defaults to text mode with universal newlines,
|
||||
# and accepts an argument to specify the text encoding
|
||||
# Python 3 only projects can skip this import
|
||||
from io import open
|
||||
|
||||
here = path.abspath(path.dirname(__file__))
|
||||
|
||||
# Get the long description from the README file
|
||||
with open(path.join(here, 'README.md'), encoding='utf-8') as f:
|
||||
long_description = f.read()
|
||||
|
||||
# Arguments marked as "Required" below must be included for upload to PyPI.
|
||||
# Fields marked as "Optional" may be commented out.
|
||||
|
||||
setup(
|
||||
# This is the name of your project. The first time you publish this
|
||||
# package, this name will be registered for you. It will determine how
|
||||
# users can install this project, e.g.:
|
||||
#
|
||||
# $ pip install sampleproject
|
||||
#
|
||||
# And where it will live on PyPI: https://pypi.org/project/sampleproject/
|
||||
#
|
||||
# There are some restrictions on what makes a valid project name
|
||||
# specification here:
|
||||
# https://packaging.python.org/specifications/core-metadata/#name
|
||||
name='codegen', # Required
|
||||
|
||||
# Versions should comply with PEP 440:
|
||||
# https://www.python.org/dev/peps/pep-0440/
|
||||
#
|
||||
# For a discussion on single-sourcing the version across setup.py and the
|
||||
# project code, see
|
||||
# https://packaging.python.org/en/latest/single_source_version.html
|
||||
version='0.1.0', # Required
|
||||
|
||||
# This is a one-line description or tagline of what your project does. This
|
||||
# corresponds to the "Summary" metadata field:
|
||||
# https://packaging.python.org/specifications/core-metadata/#summary
|
||||
# description='A sample Python project', # Optional
|
||||
|
||||
# This is an optional longer description of your project that represents
|
||||
# the body of text which users will see when they visit PyPI.
|
||||
#
|
||||
# Often, this is the same as your README, so you can just read it in from
|
||||
# that file directly (as we have already done above)
|
||||
#
|
||||
# This field corresponds to the "Description" metadata field:
|
||||
# https://packaging.python.org/specifications/core-metadata/#description-optional
|
||||
long_description=long_description, # Optional
|
||||
|
||||
# Denotes that our long_description is in Markdown; valid values are
|
||||
# text/plain, text/x-rst, and text/markdown
|
||||
#
|
||||
# Optional if long_description is written in reStructuredText (rst) but
|
||||
# required for plain-text or Markdown; if unspecified, "applications should
|
||||
# attempt to render [the long_description] as text/x-rst; charset=UTF-8 and
|
||||
# fall back to text/plain if it is not valid rst" (see link below)
|
||||
#
|
||||
# This field corresponds to the "Description-Content-Type" metadata field:
|
||||
# https://packaging.python.org/specifications/core-metadata/#description-content-type-optional
|
||||
long_description_content_type='text/markdown', # Optional (see note above)
|
||||
|
||||
# This should be a valid link to your project's main homepage.
|
||||
#
|
||||
# This field corresponds to the "Home-Page" metadata field:
|
||||
# https://packaging.python.org/specifications/core-metadata/#home-page-optional
|
||||
url='https://github.com/arukibree/CodeGen', # Optional
|
||||
|
||||
# This should be your name or the name of the organization which owns the
|
||||
# project.
|
||||
author='arukibree', # Optional
|
||||
|
||||
# This should be a valid email address corresponding to the author listed
|
||||
# above.
|
||||
# author_email='pypa-dev@googlegroups.com', # Optional
|
||||
|
||||
# Classifiers help users find your project by categorizing it.
|
||||
#
|
||||
# For a list of valid classifiers, see https://pypi.org/classifiers/
|
||||
classifiers=[ # Optional
|
||||
# How mature is this project? Common values are
|
||||
# 3 - Alpha
|
||||
# 4 - Beta
|
||||
# 5 - Production/Stable
|
||||
'Development Status :: 3 - Alpha',
|
||||
|
||||
# Indicate who your project is intended for
|
||||
'Intended Audience :: Developers',
|
||||
'Topic :: Software Development :: Build Tools',
|
||||
|
||||
# Pick your license as you wish
|
||||
'License :: OSI Approved :: MIT License',
|
||||
|
||||
# Specify the Python versions you support here. In particular, ensure
|
||||
# that you indicate whether you support Python 2, Python 3 or both.
|
||||
#'Programming Language :: Python :: 2',
|
||||
#'Programming Language :: Python :: 2.7',
|
||||
'Programming Language :: Python :: 3',
|
||||
'Programming Language :: Python :: 3.4',
|
||||
'Programming Language :: Python :: 3.5',
|
||||
'Programming Language :: Python :: 3.6',
|
||||
'Programming Language :: Python :: 3.7',
|
||||
],
|
||||
|
||||
# This field adds keywords for your project which will appear on the
|
||||
# project page. What does your project relate to?
|
||||
#
|
||||
# Note that this is a string of words separated by whitespace, not a list.
|
||||
# keywords='sample setuptools development', # Optional
|
||||
|
||||
# You can just specify package directories manually here if your project is
|
||||
# simple. Or you can use find_packages().
|
||||
#
|
||||
# Alternatively, if you just want to distribute a single Python file, use
|
||||
# the `py_modules` argument instead as follows, which will expect a file
|
||||
# called `my_module.py` to exist:
|
||||
#
|
||||
# py_modules=["my_module"],
|
||||
#
|
||||
packages=find_packages(exclude=['contrib', 'docs', 'tests']), # Required
|
||||
|
||||
# This field lists other packages that your project depends on to run.
|
||||
# Any package you put here will be installed by pip when your project is
|
||||
# installed, so they must be valid existing projects.
|
||||
#
|
||||
# For an analysis of "install_requires" vs pip's requirements files see:
|
||||
# https://packaging.python.org/en/latest/requirements.html
|
||||
install_requires=[
|
||||
'clang==5.0',
|
||||
'Mako==1.0.7'
|
||||
],
|
||||
|
||||
# List additional groups of dependencies here (e.g. development
|
||||
# dependencies). Users will be able to install these using the "extras"
|
||||
# syntax, for example:
|
||||
#
|
||||
# $ pip install sampleproject[dev]
|
||||
#
|
||||
# Similar to `install_requires` above, these must be valid existing
|
||||
# projects.
|
||||
extras_require={ # Optional
|
||||
'dev': [],
|
||||
'test': [],
|
||||
},
|
||||
|
||||
# If there are data files included in your packages that need to be
|
||||
# installed, specify them here.
|
||||
#
|
||||
# If using Python 2.6 or earlier, then these have to be included in
|
||||
# MANIFEST.in as well.
|
||||
package_data={ # Optional
|
||||
'codegen': ['data/*.mako'],
|
||||
},
|
||||
|
||||
# Although 'package_data' is the preferred approach, in some case you may
|
||||
# need to place data files outside of your packages. See:
|
||||
# http://docs.python.org/3.4/distutils/setupscript.html#installing-additional-files
|
||||
#
|
||||
# In this case, 'data_file' will be installed into '<sys.prefix>/my_data'
|
||||
# data_files=[('my_data', ['data/data_file'])], # Optional
|
||||
|
||||
# To provide executable scripts, use entry points in preference to the
|
||||
# "scripts" keyword. Entry points provide cross-platform support and allow
|
||||
# `pip` to create the appropriate form of executable for the target
|
||||
# platform.
|
||||
#
|
||||
# For example, the following would provide a command called `sample` which
|
||||
# executes the function `main` from this package when invoked:
|
||||
# entry_points={ # Optional
|
||||
# 'console_scripts': [
|
||||
# 'sample=sample:main',
|
||||
# ],
|
||||
# },
|
||||
|
||||
# List additional URLs that are relevant to your project as a dict.
|
||||
#
|
||||
# This field corresponds to the "Project-URL" metadata fields:
|
||||
# https://packaging.python.org/specifications/core-metadata/#project-url-multiple-use
|
||||
#
|
||||
# Examples listed include a pattern for specifying where the package tracks
|
||||
# issues, where the source is hosted, where to say thanks to the package
|
||||
# maintainers, and where to support the project financially. The key is
|
||||
# what's used to render the link text on PyPI.
|
||||
# project_urls={ # Optional
|
||||
# 'Bug Reports': 'https://github.com/pypa/sampleproject/issues',
|
||||
# 'Funding': 'https://donate.pypi.org',
|
||||
# 'Say Thanks!': 'http://saythanks.io/to/example',
|
||||
# 'Source': 'https://github.com/pypa/sampleproject/',
|
||||
# },
|
||||
)
|
||||
@@ -1,21 +0,0 @@
|
||||
% if Enums:
|
||||
#include <codegen/EnumReflection.h>
|
||||
% endif
|
||||
|
||||
% for Decl in ForwardDeclares:
|
||||
${Decl.GenerateText(0)}
|
||||
% endfor
|
||||
|
||||
% for Enum in Enums:
|
||||
const CEnumNameMap TEnumReflection<enum ${Enum.FullName}>::skNameMap = {
|
||||
<% ValueSet = set() %> \
|
||||
% for Constant in Enum.Constants:
|
||||
% if Constant.Value not in ValueSet:
|
||||
{ ${Constant.Value}, "${Constant.Name}" },
|
||||
<% ValueSet.add(Constant.Value) %> \
|
||||
% endif
|
||||
% endfor
|
||||
};
|
||||
const int TEnumReflection<enum ${Enum.FullName}>::skErrorValue = ${Enum.ErrorValue};
|
||||
|
||||
% endfor
|
||||
Reference in New Issue
Block a user