I using scons for a few days and confused a bit. Why there is no built-in tools for building sources recursively starting from given root? Let me explain:
I have such source disposition:
src
Core
folder1
folder2
subfolder2_1
Std
folder1
..and so on. This tree could be rather deeper.
Now I build this with such construction:
sources = Glob('./builds/Std/*/*.cpp')
sources = sources + Glob('./builds/Std/*.cpp')
sources = sources + Glob('./builds/Std/*/*/*.cpp')
sources = sources + Glob('./builds/Std/*/*/*/*.cpp')
and this looks not so perfect as at can be. Of cause, I can write some python code, but
is there more suitable ways of doing this?
As Torsten already said, there is no "internal" recursive Glob() in SCons. You need to write something yourself. My solution is:
import fnmatch
import os
matches = []
for root, dirnames, filenames in os.walk('src'):
for filename in fnmatch.filter(filenames, '*.c'):
matches.append(Glob(os.path.join(root, filename)[len(root)+1:]))
I want to stress that you need Glob() here (not glob.glob() from python) especially when you use VariantDir(). Also when you use VariantDir() don't forget to convert absolute paths to relative (in the example I achieve this using [len(root)+1:]).
Sure.
You need to write python wrappers to walking through dirs. You can find many recipes on stackoverflow.
Here is my simple function which returns list of subdirs in present dir (and ignore hide dirs starting with '.' - dot)
def getSubdirs(abs_path_dir) :
lst = [ name for name in os.listdir(abs_path_dir) if os.path.isdir(os.path.join(abs_path_dir, name)) and name[0] != '.' ]
lst.sort()
return lst
For example, i've dir modules what containts foo, bar, ice.
corePath = 'abs/path/to/modules'
modules = getSubdirs(corePath)
# modules = [bar, foo, ice]
for module in modules :
sources += Glob(os.path.join(corePath, module, '*.cpp'))
You can improve getSubdirs function adding recurse and walking deeper to subdirs.
The Glob() SCons function doesnt have the ability to go recursive.
It would be much more efficient if you change your Python code to use the list.extend() function, like this:
sources = Glob('./builds/Std/*/*.cpp')
sources.extend(Glob('./builds/Std/*.cpp'))
sources.extend(Glob('./builds/Std/*/*/*.cpp'))
sources.extend(Glob('./builds/Std/*/*/*/*.cpp'))
Instead of trying to go recursive like you are, its quite common to have a SConscript script in each subdirectory and in the root SConstruct call each of them with the SConscript() function. This is called a SCons hierarchical build.
Here is my version of recursive Glob:
from SCons.Environment import Base as BaseEnvironment
def __RGlob(self, root_path, pattern, ondisk=True, source=False, strings=False, exclude=None):
result_nodes = []
paths = [root_path]
while paths:
path = paths.pop()
all_nodes = self.Glob(f'{path}/*', ondisk=ondisk, source=source, exclude=exclude)
paths.extend(entry for entry in all_nodes if entry.isdir() or (entry.srcnode() and entry.srcnode().isdir())) # `srcnode()` must be used because `isdir()` doesn't work for entries in variant dirs which haven't been copied yet.
result_nodes.extend(self.Glob(f'{path}/{pattern}', ondisk=ondisk, source=source, strings=strings, exclude=exclude))
return sorted(result_nodes)
BaseEnvironment.RGlob = __RGlob
It tries imitate the standard Glob as closely as possible. The biggest difference is that beside the pattern is takes a root path as another argument. The pattern is then applied to this root path and every sub-directory in it.
This code adds function RGlob it to the base environment, which means you will be able to call it on every environment that is created after that. The best place to paste this is probably the file site_scons/site_init.py.
I use this:
srcdir = './'
sources = [s for s in glob2.glob(srcdir + '**/*.cpp') if "/." not in s]
Related
I have a list of file name fragments, and I want to return a list of all the file names under a certain directory.
I wrote this code to tackle this problem, but it turns out that is a rather slow solution.
train_file_list = []
for p in patient_list[:n_train]:
file = glob(os.path.join("D:\preprocess_data\Positive", p + "*.npz"))
train_file_list.append(file)
Hopefully, I can rely on your talent to come up with a more efficient way to do this. Thanks!!!
I am working on using clang bingings python to travers c/c++ code into AST,how can I get a tree based AST structure?
Some pointers on where to start, tutorials or anything in this regard will be of great help!!!
I found a very useful work(If you want to check this out ,here is the link:https://www.chess.com/blog/lockijazz/using-python-to-traverse-and-modify-clang-s-ast-tree) and tried his code,unfortunately I didn't get a useful output.
function_calls = []
function_declarations = []
def traverse(node):
for child in node.get_children():
traverse(child)
if node.type == clang.cindex.CursorKind.CALL_EXPR:
function_calls.append(node)
if node.type == clang.cindex.CursorKind.FUNCTION_DECL:
function_declarations.append(node)
print 'Found %s [line=%s, col=%s]' % (node.displayname, node.location.line, node.location.column)
clang.cindex.Config.set_library_path("/Users/tomgong/Desktop/build/lib")
index = clang.cindex.Index.create()
tu = index.parse(sys.argv[1])
root = tu.cursor
traverse(root)
Just in case anyone was having trouble still, I found that if you should be using kind instead of type
you can run clang.cindex.CursorKind.get_all_kinds() to retrieve all kinds and see that when using the node.type does not appear in any of them.
function_calls = []
function_declarations = []
def traverse(node):
for child in node.get_children():
traverse(child)
if node.kind == clang.cindex.CursorKind.CALL_EXPR:
function_calls.append(node)
if node.kind == clang.cindex.CursorKind.FUNCTION_DECL:
function_declarations.append(node)
print 'Found %s [line=%s, col=%s]' % (node.displayname, node.location.line, node.location.column)
clang.cindex.Config.set_library_path("/Users/tomgong/Desktop/build/lib")
index = clang.cindex.Index.create()
tu = index.parse(sys.argv[1])
root = tu.cursor
traverse(root)
how can I get a tree based AST structure?
The translation unit object's cursor (tu.cursor) is actually the start node of an AST. You might wanna use clang tool to visually analyze the tree. Maybe this will shed the light and give you the intuition on how to work with the tree.
clang++ -cc1 -ast-dump test.cpp
But basically, it boils down to getting children nodes of the main node (tu.cursor) and recursively traversing them, and getting to the nodes which are of interest to you.
You might wanna also check an article from Eli Benderski how to start working with the python binding:
https://eli.thegreenplace.net/2011/07/03/parsing-c-in-python-with-clang#id9
unfortunately I didn't get a useful output.
You might run into incomplete or wrong parsing, when you don't provide paths to includes in the parsed file to libclang module. For example, if the source file you want to parse uses some of the QT includes, then you need to specify relevant include paths in the parse() call like in the example here:
index = clang.cindex.Index.create()
tu = index.parse(src_file, args = [
'-I/usr/include/x86_64-linux-gnu/qt5/',
'-I/usr/include/x86_64-linux-gnu/qt5/QtCore'])
Also look for some comments in the libclang.cindex python module, they can help you. For example, I found the solution above by reading those comments.
I have been using pycparser in order to do obtain the AST of C/C++ source code and explore the same using python.
You can find the API for exploring the AST in this example from the repository.
I am using glob.glob() to read all the png files in a directory. This is what I am using now
for filename in glob.glob('D:\test_files\**\*.png',recursive=True):
...
Image files are kept in sub folders in test_files. The code will be used by someone else within another thousands of lines of codes. Hence, the directory should be defined somewhere else in the code as a string. Also I would like to change *.png by using another for loop. So, I need something like
for filename in glob.glob('imagepath'+'extension',recursive=True):
...
where imagepath is defined somewhere else in the code and extension is kept in a list. Can I use the glob.glob() method in this way?
The answer is here. I define the path like path = 'D:\test_files\**' and keep the extensions in a list like imgExtentions =['\*.jpg', '\*.png']. Then simply I use glob:
for extension in imgExtentions:
for filename in glob.glob(path+extension,recursive=True):
I sometimes want to exclude certain source files from a Glob result in SCons. Usually it's because I want to compile that source file with different options. Something like this:
objs = env.Object(Glob('*.cc'))
objs += env.Object('SpeciallyTreatedFile.cc', CXXFLAGS='-O0')
Of course, that creates a problem for SCons:
scons: *** Two environments with different actions were specified
for the same target: SpeciallyTreatedFile.o
I usually work around this using the following idiom:
objs = env.Object([f for f in Glob('*.cc')
if 'SpeciallyTreatedFile.cc' not in f.path])
But that's pretty ugly, and gets even uglier if there's more than one file to be filtered out.
Is there a clearer way to do this?
I got fed up duplicating the [f for f in Glob ...] expression in several places, so I wrote the following helper method and added it to the build Environment:
import os.path
def filtered_glob(env, pattern, omit=[],
ondisk=True, source=False, strings=False):
return filter(
lambda f: os.path.basename(f.path) not in omit,
env.Glob(pattern))
env.AddMethod(filtered_glob, "FilteredGlob");
Now I can just write
objs = env.Object(env.FilteredGlob('*.cc',
['SpeciallyTreatedFile.cc', 'SomeFileToIgnore.cc']))
objs += env.Object('SpeciallyTreatedFile.cc', CXXFLAGS='-O0')
Using this pattern it would be easy to write something similar that uses, say, a regexp filter as the omit argument instead of a simple list of filenames, but this works well for my current needs.
This is an old question but deserves an update since it still shows up on search.
SCons 2.3.5 Introduced the exclude keyword argument to Glob, such that the following is valid now:
objs = env.Object(Glob('*.cc'), exclude=['SpeciallyTreatedFile.cc'])
objs += env.Object('SpeciallyTreatedFile.cc', CXXFLAGS='-O0')
Since exclude takes a list, many excludes can be included, and the exclude list itself may contain the same kind of wildcards as the glob. The user documentation lists the following example:
sources = Glob('*.cpp', exclude=['os_*_specific_*.cpp']) + Glob('os_%s_specific_*.cpp'%currentOS)
string[] list = Directory.GetFiles("c:\\", "One Two Three - User.xml")
This code does not returns me array, but I have three directories with this file. Any ideas how to make it work?
Check out the variation of this method that takes a SearchOption, here. It seems that you're after a recursive direction search, and the SearchOption enumeration allows you to specify this.
Directory.GetFiles does not traverse subdirs in this way... so only file on C:\ is returned!!
If you need to search this pattern in a dir and in its subdirs you have to scan (recursively) all subdirs and then current dir. In every step you add files to a global variable (string[] files).
I think this example can be useful...
Or you can use Directory.GetFiles(path, pattern, SearchOption.AllDirectories);