How to generate __init__.py in all subdirectories of current directory in cmake?
Question:
I use an out-of-tree builds with CMake.
I have a CMake custom command that generates *_pb2.py files from proto-files.
Since proto-files may reside in an unknown number of subdirectories (package namespace), like $SRC/package1/package2/file.proto
, then the build directory will contain something like $BLD/package1/package2/file_pb2.py
.
I want to implicitly make packages from auto-generated *_pb2.py files and, thus, I want to automagically generate __init__.py files in all subfolders ($BLD/package1
, $BLD/package1/package2
, etc.) and then install them.
How can I do that?
P.S. I’ve tried macro from CMake : How to get the name of all subdirectories of a directory? (changed GLOB to GLOB_RECURSE) but it returns only subdirs that contain files. I can’t get package1
subdir from example above.
Answers:
If you are working under a *NIX os (including mac) you could use the shell find command like:
ROOT="./"
for DIR in $(find $ROOT -type d); do
touch $DIR/__init__.py
done
or with a python script:
from os.path import isdir, walk, join
root = "/path/to/project"
finit = '__init__.py'
def visitor(arg, dirname, fnames):
fnames = [fname for fname in fnames if isdir(fname)]
# here you could do some additional checks ...
print "adding %s to : %s" %(finit, dirname)
with open(join(dirname, finit), 'w') as file_: file_.write('')
walk(root, visitor, None)
The following should give you a list of directories as required in the variable AllPaths
:
# Get paths to all .py files (relative to build dir)
file(GLOB_RECURSE SubDirs RELATIVE ${CMAKE_BINARY_DIR} "${CMAKE_BINARY_DIR}/*.py")
# Clear the variable AllPaths ready to take the list of results
set(AllPaths)
foreach(SubDir ${SubDirs})
# Strip the filename from the path
get_filename_component(SubDir ${SubDir} PATH)
# Change the path to a semi-colon separated list
string(REPLACE "/" ";" PathParts ${SubDir})
# Incrementally rebuild path, appending each partial path to list of results
set(RebuiltPath ${CMAKE_BINARY_DIR})
foreach(PathPart ${PathParts})
set(RebuiltPath "${RebuiltPath}/${PathPart}")
set(AllPaths ${AllPaths} ${RebuiltPath})
endforeach()
endforeach()
# Remove duplicates
list(REMOVE_DUPLICATES AllPaths)
Here’s a one-line version of the other answer at https://stackoverflow.com/a/11449316/827437:
find $DIR -type d -exec touch {}/__init__.py ;
This creates an __init__.py
file within every directory in $DIR, by executing the touch
command. Run find $DIR -type d
to see the directories that will include the file.
I use an out-of-tree builds with CMake.
I have a CMake custom command that generates *_pb2.py files from proto-files.
Since proto-files may reside in an unknown number of subdirectories (package namespace), like $SRC/package1/package2/file.proto
, then the build directory will contain something like $BLD/package1/package2/file_pb2.py
.
I want to implicitly make packages from auto-generated *_pb2.py files and, thus, I want to automagically generate __init__.py files in all subfolders ($BLD/package1
, $BLD/package1/package2
, etc.) and then install them.
How can I do that?
P.S. I’ve tried macro from CMake : How to get the name of all subdirectories of a directory? (changed GLOB to GLOB_RECURSE) but it returns only subdirs that contain files. I can’t get package1
subdir from example above.
If you are working under a *NIX os (including mac) you could use the shell find command like:
ROOT="./"
for DIR in $(find $ROOT -type d); do
touch $DIR/__init__.py
done
or with a python script:
from os.path import isdir, walk, join
root = "/path/to/project"
finit = '__init__.py'
def visitor(arg, dirname, fnames):
fnames = [fname for fname in fnames if isdir(fname)]
# here you could do some additional checks ...
print "adding %s to : %s" %(finit, dirname)
with open(join(dirname, finit), 'w') as file_: file_.write('')
walk(root, visitor, None)
The following should give you a list of directories as required in the variable AllPaths
:
# Get paths to all .py files (relative to build dir)
file(GLOB_RECURSE SubDirs RELATIVE ${CMAKE_BINARY_DIR} "${CMAKE_BINARY_DIR}/*.py")
# Clear the variable AllPaths ready to take the list of results
set(AllPaths)
foreach(SubDir ${SubDirs})
# Strip the filename from the path
get_filename_component(SubDir ${SubDir} PATH)
# Change the path to a semi-colon separated list
string(REPLACE "/" ";" PathParts ${SubDir})
# Incrementally rebuild path, appending each partial path to list of results
set(RebuiltPath ${CMAKE_BINARY_DIR})
foreach(PathPart ${PathParts})
set(RebuiltPath "${RebuiltPath}/${PathPart}")
set(AllPaths ${AllPaths} ${RebuiltPath})
endforeach()
endforeach()
# Remove duplicates
list(REMOVE_DUPLICATES AllPaths)
Here’s a one-line version of the other answer at https://stackoverflow.com/a/11449316/827437:
find $DIR -type d -exec touch {}/__init__.py ;
This creates an __init__.py
file within every directory in $DIR, by executing the touch
command. Run find $DIR -type d
to see the directories that will include the file.