Skip to content

Instantly share code, notes, and snippets.

@catalan42
Created June 14, 2013 22:47
Show Gist options
  • Save catalan42/5785887 to your computer and use it in GitHub Desktop.
Save catalan42/5785887 to your computer and use it in GitHub Desktop.
Groovy program to create makefile dependencies for C/C++ or PL/I using the "touch" command.
#!/usr/bin/env groovy
import java.util.concurrent.atomic.*
class Depends {
static {
System.setProperty( 'line.separator', '\n' ); // unix format
}
final hash = '#'
final tab = '\t'
final filenameChrs = $/[-.a-zA-Z0-9\/]/$
// A case-insensitive list of source code filename suffixes which we search
// for #include or %include statements. Suffixes in this list must include
// the "." (dot) character.
final suffixesToProc = [ '.c', '.cc', '.cpp', '.cxx', '.c++',
'.h', '.hh', '.hpp', '.hxx', '.h++',
'.pli', '.pl1', '.inc' ]
boolean isRecursive
def filesToProc = []
def depSpecs = []
def fileCount = new AtomicInteger( 0 );
def showCnt = {
synchronized( fileCount ) {
if ( fileCount.get() % 20 == 0 ) {
if ( fileCount.get() % 1000 == 0 ) {
println()
print "${fileCount.get()}".padLeft(9) + " "
}
print '.'
}
fileCount.incrementAndGet()
}
}
void findFiles( File file ) {
file = new File( file.canonicalPath )
String parentName = file.parent
String lowerName = file.name.toLowerCase()
if ( lowerName.startsWith('.') ) {
return // skip hidden files
}
if ( ( isRecursive ) && ( file.isDirectory() ) ) {
file.eachFile { child -> findFiles( child ) }
} else {
suffixesToProc.each {
def lowerSuffix = it.toLowerCase()
if ( lowerName.endsWith( lowerSuffix ) ) {
filesToProc << file
return
}
}
}
}
void findDeps() {
filesToProc.each { srcFile ->
def srcText = srcFile.text
def srcLines = srcText.readLines()
showCnt()
List depFiles = []
srcLines.each { line ->
// C++ formatted includes: #include "filename.typ"
if ( line =~ /^\s*${hash}include\s+".*"/ ) {
def cppQuotedFile = $/"(${filenameChrs}+)"/$
def matcher = ( line =~ cppQuotedFile )
assert matcher.size() == 1
// There should be exactly 1 match. Because we included parens in
// our regex, the match is a single group.
def group = matcher[0]
assert group.size() == 2
// Because we had exactly 1 set of parens, the group will always be
// of length 2. The first element is the entire match. The 2nd
// element is the part inside the parens (i.e. the name of the
// include file).
def wholeMatch = group[0]
def inclName = group[1]
depFiles << inclName
}
// C++ formatted includes: #include <filename.typ>
if ( line =~ /^\s*${hash}include\s+<.*>/ ) {
def cppQuotedFile = $/<(${filenameChrs}+)>/$
def matcher = ( line =~ cppQuotedFile )
assert matcher.size() == 1
// There should be exactly 1 match. Because we included parens in
// our regex, the match is a single group.
def group = matcher[0]
assert group.size() == 2
// Because we had exactly 1 set of parens, the group will always be
// of length 2. The first element is the entire match. The 2nd
// element is the part inside the parens (i.e. the name of the
// include file).
def wholeMatch = group[0]
def inclName = group[1]
if (inclName.contains( '.' )) { // e.g. <string.h>
// Ignore 'system' includes like <iostream>, <fstream>, etc
depFiles << inclName
}
}
// PL1 formatted includes (case insensitive)
// %include 'filename.typ'
if ( line =~ /(?i)^\s*%include\s+'.*'/ ) {
def pliQuotedFile = $/'(${filenameChrs}+)'/$
def matcher = ( line =~ pliQuotedFile )
assert matcher.size() == 1
// There should be exactly 1 match. Because we included parens in
// our regex, the match is a single group.
def group = matcher[0]
assert group.size() == 2
// Because we had exactly 1 set of parens, the group will always be
// of length 2. The first element is the entire match. The 2nd
// element is the part inside the parens (i.e. the name of the
// include file).
def wholeMatch = group[0]
def inclName = group[1]
depFiles << inclName
}
} // srcLines.each
if ( depFiles.size() ) { // only write if file has dependencies
def depSpec = "${srcFile.name} :"
depFiles.each {
depSpec += " ${it}"
}
depSpecs << depSpec // target : <dependencies>
depSpecs << tab + '@ touch $@' // <tab> <update command> (silent)
}
}
}
void run( args ) {
def cli = new CliBuilder( usage:"Depends [-r|--recurse'] <files>" )
cli.h( argName: 'help', longOpt:'help', 'usage information' )
cli.r( argName: 'recurse', longOpt:'recurse', 'process files recursively' )
def options = cli.parse( args )
if (!options) { throw new RuntimeException( 'problem' ) }
def argFiles = options.arguments()
if ( (options.h) || ( !argFiles ) ) {
println """
Builds make dependencies.
"""
cli.usage()
println()
return
}
isRecursive = (options.r)
argFiles.each { name ->
findFiles( new File( name ) )
}
findDeps();
println()
println()
println " Processed ${fileCount} files"
def depSpecsFile = new File('depSpecs.mk')
println " Saved dependency specs to: " + depSpecsFile.path
depSpecsFile.withWriter { out ->
depSpecs.each {
out.writeLine it
}
}
println()
}
static main(args) {
new Depends().run( args )
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment