def generate(env): def _ghc_searchpath_opts(paths): if paths: return reduce(lambda list, path: " -i" + path, paths, "") else: return "" env["_ghc_searchpath_opts"] = _ghc_searchpath_opts def _ghc_package_opts(packages): return reduce(lambda list, package: list + " -package " + package, packages, "") env["_ghc_package_opts"] = _ghc_package_opts env["HSC"] = "ghc" env["HSCFLAGS"] = [] env["HSLINKFLAGS"] = [] env["HSSEARCHPATH"] = [] env["HSPACKAGES"] = [] env["_HSPACKAGE_OPTS"] = "${_ghc_package_opts(HSPACKAGES)}" env["_HSSEARCHPATH_OPTS"] = "${_ghc_searchpath_opts(HSSEARCHPATH)}" ghc_scanner = Scanner(function=ghc_scanner_function, skeys=[".hs", ".lhs"], path_function=ghc_path_function) ghc_c_compiler = Builder(action="$HSC $HSCFLAGS -c -o $TARGET $SOURCE", src_suffix=[".c"], suffix=".o", single_source=True) ghc_compiler = Builder( action="$HSC $HSCFLAGS $_HSSEARCHPATH_OPTS -c -o $TARGET $SOURCE", src_suffix=[".hs", ".lhs"], suffix=".o", single_source=True, emitter=ghc_emitter, source_scanner=ghc_scanner) ghc_linker = Builder( action="$HSC $HSLINKFLAGS $_HSPACKAGE_OPTS -o $TARGET $SOURCES", src_suffix=".o", suffix="$PROGSUFFIX", src_builder=[ghc_compiler, ghc_c_compiler]) ghc_make = Builder( action="$HSC --make $HSCFLAGS $_HSSEARCHPATH_OPTS -o $TARGET $SOURCE", src_suffix=[".hs", ".lhs"], suffix="", single_source=True, emitter=ghc_make_emitter, source_scanner=ghc_scanner) env.Append( BUILDERS={ "HaskellProgram": ghc_linker, "HaskellObject": ghc_compiler, "HaskellMake": ghc_make })
def __init__(self, action="yasha -o $TARGET $SOURCE"): def scan(node, env, path): src = str(node.srcnode()) src_dir = os.path.dirname(src) variant_dir = os.path.dirname(str(node)) cmd = action.replace('$SOURCE', src) cmd = ['-M'] + cmd.split()[1:] try: # Remove $TARGET from action index = cmd.index('-o') del cmd[index] del cmd[index] except ValueError: pass runner = CliRunner() result = runner.invoke(cli.cli, cmd) deps = result.output[:-1].split(" ")[2:] deps = [d.replace(src_dir, variant_dir) for d in deps] return env.File(deps) def emit(target, source, env): env.Clean(target[0], str(target[0]) + ".d") return target, source from SCons.Scanner import Scanner from SCons.Action import Action BuilderBase.__init__(self, action=Action(action), emitter=emit, source_scanner=Scanner(function=scan), single_source=True)
def add_custom_builders(env): """Call this to add all our custom builders to the environment.""" from SCons.Scanner import Scanner from SCons.Builder import Builder from SCons.Action import Action # Add the file substitution tool TOOL_SUBST(env) # XXX: Put them into tools ? env['BUILDERS']['DistutilsSharedLibrary'] = DistutilsSharedLibrary env['BUILDERS']['NumpyCtypes'] = NumpyCtypes env['BUILDERS']['DistutilsPythonExtension'] = DistutilsPythonExtension env['BUILDERS']['NumpyPythonExtension'] = NumpyPythonExtension tpl_scanner = Scanner(function=generate_from_template_scanner, skeys=['.src']) env['BUILDERS']['FromCTemplate'] = Builder( action=Action(generate_from_c_template, '$CTEMPLATECOMSTR'), emitter=generate_from_template_emitter, source_scanner=tpl_scanner) env['BUILDERS']['FromFTemplate'] = Builder( action=Action(generate_from_f_template, '$FTEMPLATECOMSTR'), emitter=generate_from_template_emitter, source_scanner=tpl_scanner) createStaticExtLibraryBuilder(env) env['BUILDERS']['DistutilsStaticExtLibrary'] = DistutilsStaticExtLibrary env['BUILDERS']['DistutilsInstalledStaticExtLibrary'] = \ DistutilsInstalledStaticExtLibrary
def generate(env): env["CYTHON"] = "cython" env["CYTHONCOM"] = "$CYTHON $CYTHONFLAGS -o $TARGET $SOURCE" env["CYTHONCFILESUFFIX"] = ".c" c_file, cxx_file = SCons.Tool.createCFileBuilders(env) c_file.suffix['.pyx'] = cython_suffix_emitter c_file.add_action('.pyx', cythonAction) c_file.suffix['.py'] = cython_suffix_emitter c_file.add_action('.py', cythonAction) create_builder(env) pyxscanner = Scanner(function=cython_scan, skeys=['.pyx'], name='PYX') env.Append(SCANNERS=[pyxscanner])
def generate(env): """Add Builders and construction variables for swig to an Environment.""" c_file, cxx_file = SCons.Tool.createCFileBuilders(env) c_file.suffix['.i'] = swigSuffixEmitter cxx_file.suffix['.i'] = swigSuffixEmitter c_file.add_action('.i', SwigAction) c_file.add_emitter('.i', _swigEmitter) cxx_file.add_action('.i', SwigAction) cxx_file.add_emitter('.i', _swigEmitter) env['SWIG'] = 'swig' env['SWIGFLAGS'] = SCons.Util.CLVar('') env['SWIGCFILESUFFIX'] = '_wrap$CFILESUFFIX' env['SWIGCXXFILESUFFIX'] = '_wrap$CXXFILESUFFIX' env['SWIGCOM'] = '$SWIG $SWIGFLAGS -o $TARGET $SOURCES' env.Append(SCANNERS=Scanner(function=_scanSwig, skeys=[".i"]))
def __init__(self, action="yasha $SOURCE -o $TARGET"): def scan(node, env, path): src = str(node.srcnode()) src_dir = os.path.dirname(src) variant_dir = os.path.dirname(str(node)) cli_command = [src, "-M"] extensions = re.search(r"(-e|--extensions)\s*(.+)", action) variables = re.search(r"(-v|--variables)\s*(.+)", action) if extensions: cli_command += ["-e", extensions.group(2)] if variables: cli_command += ["-v", extensions.group(2)] if re.match(r"--no-variables", action): cli_command += ["--no-variable-file"] if re.match(r"--no-extensions", action): cli_command += ["--no-extension-file"] runner = CliRunner() result = runner.invoke(cli.cli, cli_command) deps = result.output[:-1].split(" ")[2:] deps = [d.replace(src_dir, variant_dir) for d in deps] return env.File(deps) def emit(target, source, env): env.Clean(target[0], str(target[0]) + ".d") return target, source from SCons.Scanner import Scanner from SCons.Action import Action BuilderBase.__init__(self, action=Action(action), emitter=emit, source_scanner=Scanner(function=scan), single_source=True)
def generate(env): env["HS"] = env.Detect("ghc") or "ghc" env["HSLINK"] = "$HS $_LIBS $SOURCES -o $TARGET" env["HSCOM"] = "$HS $_IMPORTS $_LIBS -c $SOURCE -o $TARGET" env["_IMPORTS"] = "${_concat(IMPORTSPREFIX, LIBPATH, IMPORTSSUFFIX, __env__)}" env["IMPORTSPREFIX"] = "-i" env["IMPORTSSUFFIX"] = "" env["_LIBS"] = "${_concat(LIBSPREFIX, LIBS, LIBSSUFFIX, __env__)}" env["LIBSPREFIX"] = "-package " env["LIBSSUFFIX"] = "" haskellSuffixes = [".hs", ".lhs"] compileAction = SCons.Action.Action("$HSCOM") linkAction = SCons.Action.Action("$HSLINK") def addHaskellInterface(target, source, env): """ Add the .hi target with the same name as the object file. """ targetName = os.path.splitext(str(target[0]))[0] return (target + [targetName + ".hi"], source) def importedModules(node, env, path): """ Use ghc to find all the imported modules. """ #print "Figuring out dependencies for " + str(node) def removeFile(fileName, errmsg=None): """ Try to remove fileName, returns true on success, false otherwise. """ if os.path.exists(fileName): try: os.remove(fileName) except OSError: print "Unable to remove '%s'." % fileName return False return True # Generate the name of the file that is going to contain the dependency mappings. fileName = os.path.join(os.path.dirname(str(node)), "." + os.path.basename(str(node)) + ".dep") # Just in case the file already exist, to avoid the creation of a .bak file, delete it. if not removeFile(fileName): print "Dependencies will not be calculated." return [] # Build the command to obtain the dependency mapping from ghc. command = ["ghc", "-M", "-optdep-f", "-optdep" + fileName] if env._dict.has_key("LIBPATH"): command += ["-i" + string.join(env["LIBPATH"], ":")] command += [str(node)] command = string.join(command) commandIn, commandOut = os.popen4(command, "r") errorMessage = commandOut.read() commandIn.close() commandOut.read() if (errorMessage != ""): print "An error ocurred running `%s`:" % command for line in string.split(errorMessage, "\n"): print ">" + line print "Dependencies will not be calculated." removeFile(fileName) return [] try: file = open(fileName, "r") fileContents = file.read() file.close() except: print "Unable to open '%s'." % fileName print "Dependencies will not be calculated." removeFile(fileName) return [] fileContents = string.split(fileContents, "\n") deps = [] for line in fileContents: #print "deps=%s." % str(deps) if len(line) > 0 and line[0] != "#": files = string.split(line, ":") target = string.strip(files[0]) source = string.strip(files[1]) if source != str( node): # and os.path.splitext(source)[1] != ".hi": deps += [os.path.basename(source)] #if os.path.splitext(target)[0] != os.path.splitext(str(node))[0]: # deps += [os.path.basename(target)] #print " %s depends on %s." % (target, source) removeFile(fileName) #print "%s depends on %s." % (str(node), str(deps)) return deps haskellScanner = Scanner(function=importedModules, name="HaskellScanner", skeys=haskellSuffixes, recursive=False) haskellProgram = SCons.Builder.Builder(action=linkAction, prefix="$PROGPREFIX", suffix="$PROGSUFFIX", src_suffix="$OBJSUFFIX", src_builder="HaskellObject") env["BUILDERS"]["HaskellProgram"] = haskellProgram haskellLibrary = SCons.Builder.Builder(action=SCons.Defaults.ArAction, prefix="$LIBPREFIX", suffix="$LIBSUFFIX", src_suffix="$OBJSUFFIX", src_builder="HaskellObject") env["BUILDERS"]["HaskellLibrary"] = haskellLibrary haskellObject = SCons.Builder.Builder(action=compileAction, emitter=addHaskellInterface, prefix="$OBJPREFIX", suffix="$OBJSUFFIX", src_suffix=haskellSuffixes, source_scanner=haskellScanner) env["BUILDERS"]["HaskellObject"] = haskellObject
def generate(env): env.SetDefault( PYTHON=sys.executable, NODEJS='node', JS_ENGINE='$NODEJS', EMSCRIPTEN_FLAGS=['-v', '-j', '--suppressUsageWarning'], EMSCRIPTEN_TEMP_DIR=env.Dir('#/emscripten.tmp'), _expand_settings_flags=_expand_settings_flags, EMSCRIPTEN_PREJS=[], EMSCRIPTEN_POSTJS=[], EMSCRIPTEN_SETTINGS={}, _EMSCRIPTEN_SETTINGS_FLAGS= '${_expand_settings_flags(EMSCRIPTEN_SETTINGS, __env__)}', JS_OPTIMIZER_PASSES=[], LLVM_OPT_PASSES=['-std-compile-opts', '-std-link-opts'], EMSCRIPTEN_HOME=env.Dir(os.path.join(os.path.dirname(__file__), '..')), ) env.Replace( CC=os.path.join('${LLVM_ROOT}', '${CLANG}'), CXX=os.path.join('${LLVM_ROOT}', '${CLANGXX}'), AR=os.path.join('${LLVM_ROOT}', '${LLVM_LINK}'), ARCOM='$AR -o $TARGET $SOURCES', OBJSUFFIX='.bc', LIBPREFIX='', LIBSUFFIX='.bc', RANLIBCOM='', CCFLAGS=[ '-U__STRICT_ANSI__', '-target', 'le32-unknown-nacl', '-nostdinc', '-Wno-#warnings', '-Wno-error=unused-variable', '-Werror', '-Os', '-fno-threadsafe-statics', '-fvisibility=hidden', '-fvisibility-inlines-hidden', '-Xclang', '-nostdinc++', '-Xclang', '-nobuiltininc', '-Xclang', '-nostdsysteminc', '-Xclang', '-isystem$EMSCRIPTEN_HOME/system/include', '-Xclang', '-isystem$EMSCRIPTEN_HOME/system/include/libc', '-Xclang', '-isystem$EMSCRIPTEN_HOME/system/include/libcxx', '-Xclang', '-isystem$EMSCRIPTEN_HOME/system/include/bsd', '-emit-llvm' ], CXXFLAGS=['-std=c++11', '-fno-exceptions'], ) env.Append(CPPDEFINES=[ 'EMSCRIPTEN', '__EMSCRIPTEN__', '__STDC__', '__IEEE_LITTLE_ENDIAN', ]) env.Append(CPPPATH=[ env.Dir('${EMSCRIPTEN_HOME}/system/include'), ]) env['BUILDERS']['Emscripten'] = Builder( action= '$PYTHON ${EMSCRIPTEN_HOME}/emscripten.py $EMSCRIPTEN_FLAGS $_EMSCRIPTEN_SETTINGS_FLAGS --temp-dir=$EMSCRIPTEN_TEMP_DIR --compiler $JS_ENGINE --relooper=third-party/relooper.js $SOURCE > $TARGET', target_scanner=EmscriptenScanner) env['BUILDERS']['JSOptimizer'] = Builder( action= '$JS_ENGINE ${EMSCRIPTEN_HOME}/tools/js-optimizer.js $SOURCE $JS_OPTIMIZER_PASSES > $TARGET', target_scanner=EmscriptenScanner) def depend_on_embedder(target, source, env): env.Depends(target, env['JS_EMBEDDER']) return target, source def embed_files_in_js(target, source, env, for_signature): return '$PYTHON $JS_EMBEDDER $SOURCE.srcpath > $TARGET' def get_files_in_tree(node, env, path): tree_paths = [] for root, dirs, files in os.walk(str(node)): tree_paths += [os.path.join(root, f) for f in files] return [env.File(p) for p in tree_paths] env.SetDefault(JS_EMBEDDER=env.File('#/bin/embed_files_in_js.py')) FileTreeScanner = Scanner(function=get_files_in_tree, name='FileTreeScanner', recursive=False) env['BUILDERS']['EmbedFilesInJS'] = Builder(generator=embed_files_in_js, emitter=depend_on_embedder, source_scanner=FileTreeScanner) env.AddMethod(emscripten) def ConcatenateAction(target, source, env): [target] = target total = ''.join(file(str(s), 'rb').read() for s in source) file(str(target), 'wb').write(total) env['BUILDERS']['Concatenate'] = Builder(action=ConcatenateAction) libembind = build_libembind(env) libcxx = build_libcxx(env) # should embind be its own tool? env.Append( CPPPATH=['${EMSCRIPTEN_HOME}/system/include'], LIBPATH=['$EMSCRIPTEN_TEMP_DIR/internal_libs'], LIBS=[ libembind, libcxx, ], )
def get_emscripten_version_file(env): EMSCRIPTEN_HOME = env.Dir('$EMSCRIPTEN_HOME').abspath try: version_file = emscripten_version_files[EMSCRIPTEN_HOME] except KeyError: version_file = build_version_file(env) emscripten_version_files[EMSCRIPTEN_HOME] = version_file return version_file def depend_on_emscripten(node, env, path): return [get_emscripten_version_file(env)] EmscriptenScanner = Scanner(name='emscripten', function=depend_on_emscripten) def setExtension(filename, extension): return os.path.splitext(filename)[0] + '.' + extension def emscripten(env, target_js, source_bc): env = env.Clone() def buildName(extension): return setExtension(target_js, extension) # for debugging and reading generated code. # not in critical path, uses spare cores. env.LLVMDis(buildName('ll'), source_bc)
def generate(env): env["ERLC"] = env.Detect("erlc") or "erlc" env["ERL"] = env.Detect("erl") or "erl" bugReport = ''' Please report this bug via the SCons Erlang tool project issue tracker on BitBucket ( cf. https://bitbucket.org/russel/scons_erlang) or direct to Russel Winder <*****@*****.**>.''' def addTarget(target, source, env): """ Adds the targets (.beam, .script and/or .boot) according to source's extension, source's path and $OUTPUT. """ # We should receive one and only one source. if len(source) > 1: print("Warning: unexpected internal situation.") print("This is a bug. {}".format(bugReport)) print("addTarget received more than one source.") print("addTarget({}, {}, {})".format(source, target, env)) sourceStr = str(source[0]) # Tear appart the source. filename = os.path.basename(sourceStr) extension = os.path.splitext(filename)[1] basename = os.path.splitext(filename)[0] # Use $OUTPUT or where the source is as the prefix. prefix = outputDir(sourceStr, env) # Generate the targen according to the source. if extension == ".erl": # .erls generate a .beam. return ([prefix + basename + ".beam"], source) elif extension == ".rel": # .rels generate a .script and a .boot. return ([ prefix + basename + ".script", prefix + basename + ".boot" ], source) else: print("Warning: extension '{}' is unknown.".format(extension)) print( "If you feel this is a valid extension, then it might be a missing feature or a bug. {}" .format(bugReport)) print("addTarget({}, {}, {}).".format(target, source, env)) return (target, source) def erlangGenerator(source, target, env, for_signature): """ Generate the erlc compilation command line. """ # We should receive one and only one source. if len(source) > 1: print("Warning: unexpected internal situation.") print("This is a bug. {}".format(bugReport)) print("erlangGenerator received more than one source.") print("erlangGenerator({}, {}, {}, {})".format( source, target, env, for_signature)) source = str(source[0]) # Start with the complier. command = "$ERLC" # The output (-o) parameter command += " -o " + outputDir(source, env) # Add the libpaths. if env.has_key("ERLLIBPATH"): if not isinstance(env["ERLLIBPATH"], list): env["ERLLIBPATH"] = [env["ERLLIBPATH"]] for libpath in env["ERLLIBPATH"]: command += " -I " + libpath # At last, the source. return command + " " + source erlangBuilder = Builder( generator=erlangGenerator, #action = "$ERLC -o $OUTPUT $SOURCE", #suffix = [".beam", ".boot", ".script"], src_suffix=".erl", emitter=addTarget, single_source=True) env.Append(BUILDERS={"Erlang": erlangBuilder}) env.Append(ENV={"HOME": os.environ["HOME"]}) # erlc needs $HOME. def outputDir(source, env): """ Given a source and its environment, return the output directory. """ if env.has_key("OUTPUT"): return env["OUTPUT"] else: return dirOf(source) def libpath(env): """ Return a list of the libpath or an empty list. """ if env.has_key("ERLLIBPATH"): if isinstance(env["ERLLIBPATH"], list): return env["ERLLIBPATH"] else: return [env["ERLLIBPATH"]] else: return [] def dirOf(filename): """ Returns the relative directory of filename. """ directory = os.path.dirname(filename) if directory == "": return "./" else: return directory + "/" def relModules(node, env, path): """ Return a list of modules needed by a release (.rel) file. """ # Run the function reApplications of erlangscanner to get the applications. command = "erl -noshell -s erlangscanner relApplications \"" + str( node) + "\" -s init stop" sp = subprocess.Popen(command, shell=True, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE) sp.wait() if sp.returncode != 0: print( "Warning: The scanner failed to scan your files, dependencies won't be calculated." ) print( "If your file '{}' is correctly (syntactically and semantically), this is a bug. {}" .format((node, bugReport))) print("Command: {}.".format(command)) print("Return code: {}.".format(sp.returncode)) print("Output: \n{}\n".format(sp.stdout.read().strip())) print("Error: \n{}\n".format(sp.stderr.read().strip())) return [] # Get the applications defined in the .rel. appNames = sp.stdout.read().split() # Build the search path paths = set([outputDir(str(node), env)] + libpath(env)) modules = [] for path in paths: for appName in appNames: appFileName = path + appName + ".app" if os.access(appFileName, os.R_OK): modules += appModules(appFileName, env, path) return modules def appModules(node, env, path): """ Return a list of modules needed by a application (.app) file. """ # Run the function appModules of erlangscanner to get the modules. command = "erl -noshell -s erlangscanner appModules \"" + str( node) + "\" -s init stop" sp = subprocess.Popen(command, shell=True, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE) sp.wait() if sp.returncode != 0: print( "Warning: The scanner failed to scan your files, dependencies won't be calculated." ) print( "If your file '{}' is correctly (syntactically and semantically), this is a bug. {}" .format(node, bugReport)) print("Command: {}.".format(command)) print("Return code: {}.".format(sp.returncode)) print("Output: \n{}\n".format(sp.stdout.read().strip())) print("Error: \n{}\n".format(sp.stderr.read().strip())) return [] # Get the applications defined in the .rel. moduleNames = sp.stdout.read().split() # Build the search path paths = set([outputDir(node, env)] + libpath(env)) modules = [] # When there are more than one application in a project, since we are scanning all paths against all files, we might end up with more dependencies that really exists. The worst is that we'll get recompilation of a file that didn't really needed it. for path in paths: for moduleName in moduleNames: modules.append(moduleName + ".beam") return modules relScanner = Scanner(function=relModules, name="RelScanner", skeys=[".rel"], recursive=False) env.Append(SCANNERS=relScanner) def edocGenerator(source, target, env, for_signature): """ Generate the command line to generate the code. """ tdir = os.path.dirname(str(target[0])) + "/" command = "erl -noshell -run edoc_run files '[%s]' '[{dir, \"%s\"}]' -run init stop" % ( ",".join(['"' + str(x) + '"' for x in source]), tdir) return command def documentTargets(target, source, env): """ Artifitially create all targets that generating documentation will generate to clean them up latter. """ tdir = os.path.dirname(str(target[0])) + "/" newTargets = [str(target[0])] # TODO: What happens if two different sources has the same name on different directories ? newTargets += [ tdir + os.path.splitext(os.path.basename(filename))[0] + ".html" for filename in map(str, source) ] newTargets += [ tdir + filename for filename in [ "edoc-info", "modules-frame.html", "overview-summary.html", "overview-summary.html", "stylesheet.css", "packages-frame.html" ] ] #newSources = source + [tdir + "overview.edoc"] return (newTargets, source) def edocScanner(node, env, path): #print "edocScanner(%s, %s, %s)\n" % (node, env, path) overview = os.path.dirname(str(node)) + "/overview.edoc" if os.path.exists(overview): return ["overview.edoc"] else: return [] edocBuilder = Builder(generator=edocGenerator, emitter=documentTargets, target_scanner=Scanner(function=edocScanner)) env.Append(BUILDERS={"EDoc": edocBuilder})
def generate(env): scan = Scanner(function=scan_dependencies, skeys=".js") env.Append(SCANNERS=scan) env.AddMethod(ConcatJS)
# right, platform-specific extension for "schemescanner". eg. ".exe" # on Windows. # cmd = '%s %s' % (env['schemescanner'] + env.get('EXESUFFIX', ""), str(node)) cmd = '%s %s' % (env['schemescanner'], str(node)) stringresults = exe(cmd) # print "RESULTS ", results results_split = re.split(' ', stringresults) for i in results_split: if i != '': filtered.append(i) # print "RESULTS SPLIT ", filtered return filtered SchemeScanner = Scanner(function=schemefile_scan, skeys=['.scm', '.ss']) mzc = Builder(action=Action('$MZCCOM', '$MZCCOMSTR'), src_suffix=".scm", single_source=True, source_scanner=SchemeScanner) def generate(env, *kw): env['BUILDERS']['Mzc'] = mzc env['MZC'] = 'mzc' env['MZCCOM'] = '$MZC --exe $TARGET $SOURCE' def exists(env): return 1
def generate(env): bugReport = "Please report it to Pupeno <*****@*****.**> (http://pupeno.com/software/scons-erlang)." env["ERLC"] = env.Detect("erlc") env["ERL"] = env.Detect("erl") env["RUN_ERL"] = env.Detect("run_erl") env["ERL_CALL"] = env.Detect("erl_call") # The following might be useful, but it is actually needed before the environment is built, so, it is useless. # Find the Erlang root. #command = "erl -noshell -s erlangscanner erlang_dir -s init stop" #sp = subprocess.Popen(command, # shell = True, # stdin = None, # stdout = subprocess.PIPE, # stderr = subprocess.PIPE) #sp.wait() #if sp.returncode != 0: # print("Warning: Failed to find Erlang's root directory, falling back to a default.") # print("If you have a correct Erlang installation (and it seems likely if you reached this point) and you are not compiling SCons Erlang itself righ now, this is likely to be a bug. %s" % bugReport) # print("Command: %s." % command) # print("Return code: %s." % sp.returncode) # print("Output: \n%s\n" % sp.stdout.read().strip()) # print("Error: \n%s\n" % sp.stderr.read().strip()) # env["ERLROOTDIR"] = "/usr/local/lib/erlang" #else: # env["ERLROOTDIR"] = sp.stdout.read() # #env["ERLLIBDIR"] = "$ERLROOTDIR/lib" ################################################## ##### Erlang Builder methods and definitions ##### def addTarget(target, source, env): """ When the source is a .erl file the target is a .beam file of the same base name and on the output directory according to the user preferences. When the source is a .rel file the targets are a .script and a .boot files with the same rules as for .erl files. For everything else just compalain. If the user chooses to set the output to false disabling automating handling of the output, this function will be useless. The goal of this function is to be used as emitter on the Erlang builder, it make SCons aware of what files to clean when cleaning. """ # We should receive one and only one source. if len(source) > 1: print("Warning: unexpected internal situation.") print("This is a bug. %s" % bugReport) print("addTarget received more than one source.") print("addTarget(%s, %s, %s)" % (source, target, env)) sourceStr = str(source[0]) # Tear appart the source. filename = os.path.basename(sourceStr) extension = os.path.splitext(filename)[1] basename = os.path.splitext(filename)[0] # Get the directory where the output is going to end. output_dir = outputDir(sourceStr, env) # If the output_dir is False the user doesn't want us to do automatic handling of the output. if output_dir: # Generate the targen according to the source. if extension == ".erl": # .erls generate a .beam. return ([output_dir + basename + ".beam"], source) elif extension == ".rel": # .rels generate a .script and a .boot. return ([output_dir + basename + ".script", output_dir + basename + ".boot"], source) else: print("Warning: extension '%s' is unknown." % extension) print("If you feel this is a valid extension, then it might be a missing feature or a bug. %s" % bugReport) print("addTarget(%s, %s, %s)." % (target, source, env)) return (target, source) else: return (target, source) def erlangGenerator(source, target, env, for_signature): """ Generate the erlc compilation command line. The purpose of this function is to be used in the Erlang builder. """ # We should receive one and only one source. if len(source) > 1: print("Warning: unexpected internal situation.") print("This is a bug. %s" % bugReport) print("erlangGenerator received more than one source.") print("erlangGenerator(%s, %s, %s, %s)" % (source, target, env, for_signature)) # Get the source name as a string. source = str(source[0]) # Do we have any path to prepend ? if env.has_key("PATHPREPEND"): if isinstance(env["PATHPREPEND"], str): path_prepend = [env["PATHPREPEND"]] else: path_prepend = env["PATH_PREPEND"] else: path_prepend = [] # Do we have any path to append ? if env.has_key("PATHAPPEND"): if isinstance(env["PATHAPPEND"], str): path_append = [env["PATHAPPEND"]] else: path_append = env["PATHAPPEND"] else: path_append = [] # Get the output directory to be used or False if no automatic output handling is going to be used. output_dir = outputDir(source, env) # Start with the complier. command = "$ERLC $ERLFLAGS" # Add the output statment if it's being used. if output_dir: command += " -o " + output_dir path_prepend.append(output_dir) # Path preppend. if path_prepend: arg = " -pa " command += arg + arg.join(path_prepend) + " " # Path append. if path_append: arg = " -pz " command += arg + arg.join(path_append) + " " # Add the libpaths. if env.has_key("LIBPATH"): if not isinstance(env["LIBPATH"], list): env["LIBPATH"] = [env["LIBPATH"]] for libpath in env["LIBPATH"]: command += " -I " + libpath # At last, the source. return command + " " + source erlangBuilder = Builder(generator = erlangGenerator, #suffix = [".beam", ".boot", ".script"], src_suffix = ".erl", emitter = addTarget, single_source = True) env.Append(BUILDERS = {"Erlang" : erlangBuilder}) env.Append(ENV = {"HOME" : os.environ["HOME"]}) # erlc needs $HOME. ############################################################### ##### Erlang Release file scanner methods and definitions ##### def erlangRelScanner(node, env, path): """ Erlang Release file scanner. Returns the list of modules needed by a .rel file to be compiled into the .script and .boot files. """ # Get the name of the release file as a string. rel = str(node) # Build the search path for applications and modules. output_dir = outputDir(rel, env) if output_dir: paths = set([output_dir] + libpath(env)) else: paths = set(libpath(env)) # Find the applications needed by the release. apps = appNeededByRel(rel, paths) modules = [] # Find the modules needed by each application. for app in apps: mods = modNeededByApp(app, paths) modules += mods return modules def appNeededByRel(rel, paths): """ Return a list of applications (.app files) needed by a release (.rel file). """ # Run the function relApplications of erlangscanner to get the applications. command = "erl -noshell -s erlangscanner app_needed_by_rel \"%s\" -s init stop" % rel sp = subprocess.Popen(command, shell = True, stdin = None, stdout = subprocess.PIPE, stderr = subprocess.PIPE) sp.wait() if sp.returncode != 0: print("Warning: The scanner failed to scan your files, dependencies won't be calculated.") print("If your file '%s' is correctly (syntactically and semantically), this is a bug. %s" % (rel, bugReport)) print("Command: %s." % command) print("Return code: %s." % sp.returncode) print("Output: \n%s\n" % sp.stdout.read().strip()) print("Error: \n%s\n" % sp.stderr.read().strip()) return [] # Get the applications defined in the .rel. appNames = sp.stdout.read().split() modules = [] for path in paths: for appName in appNames: appFileName = path + appName + ".app" if os.access(appFileName, os.R_OK): modules.append(appFileName) #modules += appModules(appFileName, env, path) return modules def modNeededByApp(app, paths): """ Return a list of modules (.beam files) needed by a application (.app file). """ # Run the function appModules of erlangscanner to get the modules. command = "erl -noshell -s erlangscanner mod_needed_by_app \"%s\" -s init stop" % app sp = subprocess.Popen(command, shell = True, stdin = None, stdout = subprocess.PIPE, stderr = subprocess.PIPE) sp.wait() if sp.returncode != 0: print("Warning: The scanner failed to scan your files, dependencies won't be calculated.") print("If your file '%s' is correctly (syntactically and semantically), this is a bug. %s" % (app, bugReport)) print("Command: %s." % command) print("Return code: %s." % sp.returncode) print("Output: \n%s\n" % sp.stdout.read().strip()) print("Error: \n%s\n" % sp.stderr.read().strip()) return [] # Get the applications defined in the .rel. moduleNames = sp.stdout.read().split() modules = [] # When there are more than one application in a project, since we are scanning all paths against all files, we might end up with more dependencies that really exists. The worst is that we'll get recompilation of a file that didn't really needed it. for path in paths: for moduleName in moduleNames: modules.append(os.path.abspath(path + moduleName +".beam")) return modules erlangRelScanner = Scanner(function = erlangRelScanner, name = "ErlangRelScanner", skeys = [".rel"], recursive = False) env.Append(SCANNERS = erlangRelScanner) ####################################################### ##### Erlang EDoc builder methods and definitions ##### def edocFilesGenerator(source, target, env, for_signature): """ Generate the command line to generate the code. """ options = [] if env.has_key("DIR"): options.append(("dir", strInStr(env["DIR"]))) else: # Automatic generation of dir based on the target. options.append(("dir", strInStr("doc/"))) if env.has_key("DEF"): defs = map(lambda (key, value): pyTupleToErlTulpe((key, strInStr(value))), env["DEF"]) options.append(("def", "[" + ",".join(defs) + "]")) options = "[" + ",".join(map(pyTupleToErlTulpe, options)) + "]" command = "erl -noshell -run edoc_run files '[%s]' '%s'" % (",".join(['"' + str(x) + '"' for x in source]), options) return command def documentTargets(target, source, env): """ Artifitially create all targets that generating documentation will generate to clean them up latter. """ # Find the output dir. if env.has_key("DIR"): dir = env["DIR"] else: dir = "doc/" # TODO: What happens if two different sources has the same name on different directories ? target = [dir + os.path.splitext(os.path.basename(filename))[0] + ".html" for filename in map(str, source)] target += [dir + filename for filename in ["edoc-info", "index.html", "modules-frame.html", "overview-summary.html", "stylesheet.css", "packages-frame.html"]] #newSources = source + [tdir + "overview.edoc"] return (target, source) def edocScanner(node, env, path): overview = os.path.dirname(str(node)) + "/overview.edoc" if os.path.exists(overview): return ["overview.edoc"] else: return [] edocFilesBuilder = Builder(generator = edocFilesGenerator, emitter = documentTargets, target_scanner = Scanner(function=edocScanner)) env.Append(BUILDERS = {"EDocFiles" : edocFilesBuilder}) ################################################ ##### EUnit runner methods and definitions ##### ########################## ##### Helper Methods ##### def outputDir(source, env): """ Given a source and its environment, return the output directory. The OUTPUTDIR environment variable will be checked first, if it is set to False this function returns False (the user wants us out of the way), otherwise the directory of the OUTPUTDIR will be used. If the variable is not set the output directory will be calculate from the source of the file, which is just the directory where the source is unless it ends in src/ in which case the ebin/ counterpart would be used. """ if env.has_key("OUTPUTDIR"): if env["OUTPUTDIR"]: if env["OUTPUTDIR"][-1] != "/": return env["OUTPUTDIR"] + "/" else: return env["OUTPUTDIR"] else: False else: output = dirOf(source) if output[-4:] == "src/": return output[:-4] + "ebin/" else: return output def libpath(env): """ Return a list of the libpath or an empty list. """ if env.has_key("LIBPATH"): if isinstance(env["LIBPATH"], list): return env["LIBPATH"] else: return [env["LIBPATH"]] else: return [] def dirOf(filename): """ Returns the relative directory of filename. """ directory = os.path.dirname(filename) if directory == "": return "./" else: return directory + "/" def strInStr(str): """ Put a string inside a string, that is, the string will contain leading and trailing double quotes so it is a string containing an Erlang string (otherwise it would be a string containing possible an atom, variable name or something just invalid). """ return '"%s"' % str def pyTupleToErlTulpe(tpl): """ Turn a Python tuple into a string containing a valid Erlang tuple. """ return "{" + ",".join(map(str, tpl)) + "}"
files = [m.replace('.', '/') + '.pxd' for m in matches] files += [m.replace('.', '/') + '.pyx' for m in matches] # cdef extern from <file> files += cdef_import_re.findall(contents) # Handle relative imports cur_dir = str(node.get_dir()) files = [cur_dir + f if f.startswith('/') else f for f in files] # Filter out non-existing files (probably system imports) files = [f for f in files if env.File(f).exists()] return env.File(files) pyxscanner = Scanner(function=pyx_scan, skeys=['.pyx', '.pxd'], recursive=True) cythonAction = Action("$CYTHONCOM") def create_builder(env): try: cython = env['BUILDERS']['Cython'] except KeyError: cython = SCons.Builder.Builder(action=cythonAction, emitter={}, suffix=cython_suffix_emitter, single_source=1) env.Append(SCANNERS=pyxscanner) env['BUILDERS']['Cython'] = cython return cython
def generate(env, **kw): occbuild = kw.get('occbuild', None) if occbuild: occbuild_path = occbuild[0].abspath depend_emitter = lambda target, source, env: occbuild_depend_emitter( target, source, env, occbuild) else: occbuild_path = 'occbuild' depend_emitter = None pideps_scanner = Scanner(function=pideps_scan, skeys=['.occ'], path_function=FindPathDirs('INCPATH')) tce_bld = Builder(action=Action('$OCCBUILDCOM', '$OCCBUILDCOMSTR'), emitter=depend_emitter, suffix='.tce', src_suffix='.occ') # FIXME: The source scanner does not work well enough yet :/ #source_scanner = pideps_scanner) lib_bld = Builder(action=Action('$OCCBUILDLIBRARYCOM', '$OCCBUILDLIBRARYCOMSTR'), emitter=[depend_emitter, occbuild_library_emitter], suffix='.lib', src_suffix='.tce', src_builder=[tce_bld]) prog_bld = Builder( action=Action('$OCCBUILDPROGRAMCOM', '$OCCBUILDPROGRAMCOMSTR'), emitter=[depend_emitter, occbuild_program_emitter], suffix='$PROGSUFFIX', src_suffix=['.occ', '.tce'], # FIXME: If I leave the sourcebuilder in, scons seems to # want to turn my .occ extensions when I have a mixed # .occ, .tce source list into .tce using the builder ) #src_builder = [tce_bld]) tbc_headr_bld = Builder(action=Action('$TBCHEADERCOM', '$TBCHEADERCOMSTR'), emitter=[depend_emitter], suffix='.h', src_suffix=['.occ', '.tce'], src_bulider=[tce_bld]) # Add the new Builder to the list of builders # Use of $( $) causes bracketed flags not trigger rebuild when changed env['BUILDERS']['OccamObject'] = tce_bld env['OCCBUILDCOM'] = '$OCCBUILD $_OCCBUILD_TOOLCHAIN $_OCCBUILD_SEARCH_DIRS $OCCBUILDFLAGS --object $SOURCES' env['BUILDERS']['OccamLibrary'] = lib_bld env['OCCBUILDLIBRARYCOM'] = '$OCCBUILD $_OCCBUILD_TOOLCHAIN $_OCCBUILD_SEARCH_DIRS $OCCBUILDFLAGS --library $TARGET $SOURCES' env['BUILDERS']['OccamProgram'] = prog_bld env['OCCBUILDPROGRAMCOM'] = '$OCCBUILD $_OCCBUILD_TOOLCHAIN $_OCCBUILD_SEARCH_DIRS $OCCBUILDFLAGS --program $SOURCES' env['BUILDERS']['OccamBytecodeHeader'] = tbc_headr_bld env['TBCHEADERCOM'] = '$SKROC $_SKROC_SEARCH_DIRS $SKROCFLAGS --c -f $TARGET $SOURCES' env['OCCBUILD'] = occbuild_path env['_OCCBUILD_SEARCH_DIRS'] = '$( ${_concat(OCCBUILD_SEARCH_PFX, INCPATH, "", __env__, RDirs, TARGET, SOURCE)} $)' env['_SKROC_SEARCH_DIRS'] = '$( ${_concat(SKROC_SEARCH_PFX, INCPATH, "", __env__, RDirs, TARGET, SOURCE)} $)' env['OCCBUILD_SEARCH_PFX'] = '--search ' env['SKROC_SEARCH_PFX'] = '-L ' env['OCCBUILD_TOOLCHAIN'] = None env['_OCCBUILD_TOOLCHAIN'] = '${(OCCBUILD_TOOLCHAIN and "--toolchain $OCCBUILD_TOOLCHAIN" or "")}' def OccLibDepend(self, node, lib_name): if not isinstance(lib_name, list): list(lib_name) for lib in lib_name: self.Depends(node, self['OCCLIBS'][lib]['dep']) if 'inc' in self['OCCLIBS'][lib]: for n in node: n.env.AppendUnique(INCPATH=self['OCCLIBS'][lib]['inc']) env.AddMethod(OccLibDepend) env['OCCLIBS'] = dict() env['INCPATH'] = CLVar('') env['OCCBUILDFLAGS'] = CLVar('')
accum = '' command = ['$OCAMLDEP', '$_OCAML_PATH', '$_OCAML_PP', str(node)] [command] = env.subst_list(command) deps = env.ReadPipe(command) deps = joinLines(deps) deps = imap(str.split, deps) deps = (fields[2:] for fields in deps if fields[0] == target) deps = chain(*deps) deps = imap(env.File, deps) return deps __ocamldep_scanner = Scanner( function=__ocamldep_scan, name='ocamldep_scanner', node_class=File, skeys=['.mli', '.ml'], ) ######################################################################## # # Object file builder # __lex_action = Action([['$OCAMLLEX', '-o', '$TARGET', '$SOURCE']]) __lex_builder = Builder( src_suffix='.mll', single_source=True, action=__lex_action, suffix='.ml',
) if package is not None: result.append(package) continue # Check for a build result package = env.FindFile( package_name + os.path.extsep + env['GO_ARCHNAME'], subpaths, ) if package is not None: result.append(package) continue return result go_scanner = Scanner(function=_go_scan_func, skeys=['.go']) def _gc_emitter(target, source, env): if env['GO_STRIPTESTS']: return (target, [s for s in source if not str(s).endswith('_test.go')]) else: return (target, source) def _ld_scan_func(node, env, path): obj_suffix = os.path.extsep + env['GO_ARCHNAME'] result = [] for child in node.children(): if str(child).endswith(obj_suffix) or str(child).endswith('.a'): result.append(child)
def generate(env): def depend_on_combiner(target, source, env): env.Depends(target, env['MODULE_COMBINE']) env.Depends(target, env['MODULE_SCAN']) return target, source def combine(target, source, env, for_signature): aliases = [ "--alias %s=%s" % (key, value) for key, value in env['MODULE_ALIASES'].items() ] module_combine = os.path.relpath( env.subst('$MODULE_COMBINE'), env['MODULE_COMBINE'].cwd or os.getcwd()) return '$NODEJS ' + module_combine + ' ' + ' '.join( aliases) + ' $SOURCE > $TARGET' path = os.path.join(os.path.relpath(os.path.dirname(__file__)), '..', 'bin', 'combine.js') env['MODULE_COMBINE'] = env.File(path) path = os.path.join(os.path.relpath(os.path.dirname(__file__)), '..', 'bin', 'scan-dependencies.js') env['MODULE_SCAN'] = env.File(path) env['MODULE_ALIASES'] = {} def scan_module_dependencies(node, env, path): # TODO: maybe we should pass the list of aliases and loaders to the tool rather than parsing the @ here import os module_scan = os.path.relpath(env.subst('$MODULE_SCAN'), env['MODULE_SCAN'].cwd or os.getcwd()) cmd = [env.subst('$NODEJS'), module_scan, str(node)] popen = subprocess.Popen(cmd, stdout=subprocess.PIPE) stdout, _ = popen.communicate() if popen.returncode: raise AssertionError( 'scan-dependencies failed with return code %r' % (popen.returncode, )) def resolveAlias(path): if path.startswith('@'): a = path[1:] try: return env['MODULE_ALIASES'][a] except KeyError: return None else: return path paths = filter(None, stdout.split('\n')) paths = [path.replace('\\', '/') for path in paths] paths = filter(lambda s: '!' not in s, paths) paths = filter(None, map(resolveAlias, paths)) return map(env.File, paths) ModuleScanner = Scanner(function=scan_module_dependencies, name='ModuleScanner', recursive=False) CombinedModule = Builder(generator=combine, emitter=depend_on_combiner, source_scanner=ModuleScanner) env.Append(BUILDERS={'CombinedModule': CombinedModule})