blob: 6e423691ecda557e293833ff8fc2d704c07dfbd3 [file] [log] [blame]
#!/usr/bin/env python
#
# The LLVM Compiler Infrastructure
#
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
#
##===----------------------------------------------------------------------===##
#
# This script attempts to be a drop-in replacement for gcc.
#
##===----------------------------------------------------------------------===##
import os
import sys
import subprocess
def checkenv(name, alternate=None):
"""checkenv(var, alternate=None) - Return the given environment var,
or alternate if it is undefined or empty."""
v = os.getenv(name)
if v and v.strip():
return v.strip()
return alternate
def checkbool(name, default=False):
v = os.getenv(name)
if v:
try:
return bool(int(v))
except:
pass
return default
CCC_LOG = checkenv('CCC_LOG')
CCC_ECHO = checkbool('CCC_ECHO')
CCC_NATIVE = checkbool('CCC_NATIVE','1')
CCC_FALLBACK = checkbool('CCC_FALLBACK')
CCC_LANGUAGES = checkenv('CCC_LANGUAGES','c,c++,c-cpp-output,objective-c,objective-c++,objective-c-cpp-output,assembler-with-cpp')
if CCC_LANGUAGES:
CCC_LANGUAGES = set([s.strip() for s in CCC_LANGUAGES.split(',')])
# We want to support use as CC or LD, so we need different defines.
CLANG = checkenv('CLANG', 'clang')
LLC = checkenv('LLC', 'llc')
AS = checkenv('AS', 'as')
CC = checkenv('CCC_CC', 'cc')
LD = checkenv('CCC_LD', 'c++')
def error(message):
print >> sys.stderr, 'ccc: ' + message
sys.exit(1)
def quote(arg):
if '"' in arg or ' ' in arg:
return repr(arg)
return arg
def stripoutput(args):
"""stripoutput(args) -> (output_name, newargs)
Remove the -o argument from the arg list and return the output
filename and a new argument list. Assumes there will be at most
one -o option. If no output argument is found the result is (None,
args)."""
for i,a in enumerate(args):
if a.startswith('-o'):
if a=='-o':
if i+1<len(args):
return args[i+1],args[:i]+args[i+2:]
elif a.startswith('-o='):
opt,arg = a.split('=',1)
return arg,args[:i]+args[i+1:]
return None,args
def run(args):
if CCC_ECHO:
print ' '.join(map(quote, args))
sys.stdout.flush()
code = subprocess.call(args)
if code > 255:
code = 1
if code:
sys.exit(code)
def remove(path):
"""remove(path) -> bool - Attempt to remove the file at path (if any).
The result indicates if the remove was successful. A warning is
printed if there is an error removing the file."""
if os.path.exists(path):
try:
os.remove(path)
except:
print >>sys.stderr, 'WARNING: Unable to remove temp "%s"'%(path,)
return False
return True
def preprocess(args):
command = [CLANG,'-E']
run(command + args)
def syntaxonly(args):
command = [CLANG,'-fsyntax-only']
run(command + args)
def compile_fallback(args):
command = [CC,'-c']
run(command + args)
def compile(args, native, save_temps=False):
if native:
output,args = stripoutput(args)
if not output:
raise ValueError,'Expected to always have explicit -o in compile()'
# I prefer suffixing these to changing the extension, which is
# more likely to overwrite other things. We could of course
# use temp files.
bc_output = output + '.bc'
s_output = output + '.s'
command = [CLANG,'-emit-llvm-bc']
try:
run(command + args + ['-o', bc_output])
# FIXME: What controls relocation model?
run([LLC, '-relocation-model=pic', '-f', '-o', s_output, bc_output])
run([AS, '-o', output, s_output])
finally:
if not save_temps:
remove(bc_output)
remove(s_output)
else:
command = [CLANG,'-emit-llvm-bc']
run(command + args)
def checked_compile(args, native, language, save_temps):
if CCC_LANGUAGES and language and language not in CCC_LANGUAGES:
log('fallback', args)
print >>sys.stderr, 'NOTE: ccc: Using fallback compiler for: %s'%(' '.join(map(quote, args)),)
compile_fallback(args)
elif CCC_FALLBACK:
try:
compile(args, native, save_temps)
except:
log('fallback-on-fail', args)
print >>sys.stderr, 'WARNING: ccc: Using fallback compiler for: %s'%(' '.join(map(quote, args)),)
compile_fallback(args)
else:
compile(args, native, save_temps)
def link(args, native):
if native:
run([LD] + args)
else:
command = ['llvm-ld', '-native', '-disable-internalize']
run(command + args)
def extension(path):
return path.split(".")[-1]
def changeextension(path, newext):
i = path.rfind('.')
if i < 0:
return path
j = path.rfind('/', 0, i)
if j < 0:
return path[:i] + "." + newext
return path[j+1:i] + "." + newext
def inferlanguage(extension):
if extension == "c":
return "c"
elif extension in ["cpp", "cc"]:
return "c++"
elif extension == "i":
return "c-cpp-output"
elif extension == "m":
return "objective-c"
elif extension == "mm":
return "objective-c++"
elif extension == "mi":
return "objective-c-cpp-output"
elif extension == "s":
return "assembler"
elif extension == "S":
return "assembler-with-cpp"
else:
return ""
def log(name, item):
if CCC_LOG:
f = open(CCC_LOG,'a')
print >>f, (name, item)
f.close()
def inferaction(args):
if '-E' in args:
return 'preprocess'
if '-fsyntax-only' in args:
return 'syntax-only'
if '-c' in args:
return 'compile'
for arg in args:
if arg.startswith('-print-prog-name'):
return 'pring-prog-name'
return 'link'
def main(args):
log('invoke', args)
action = inferaction(args)
output = ''
compile_opts = []
link_opts = []
files = []
save_temps = 0
language = ''
native = CCC_NATIVE
i = 0
while i < len(args):
arg = args[i]
if '=' in arg:
argkey,argvalue = arg.split('=',1)
else:
argkey,argvalue = arg,None
# Modes ccc supports
if arg == '-save-temps':
save_temps = 1
if arg == '-emit-llvm' or arg == '--emit-llvm':
native = False
# Options with no arguments that should pass through
if arg in ['-v', '-fobjc-gc', '-fobjc-gc-only', '-fnext-runtime',
'-fgnu-runtime']:
compile_opts.append(arg)
link_opts.append(arg)
# Options with one argument that should be ignored
if arg in ['--param', '-u']:
i += 1
# Preprocessor options with one argument that should be ignored
if arg in ['-MT', '-MF']:
i += 1
# Prefix matches for the compile mode
if arg[:2] in ['-D', '-I', '-U', '-F']:
if not arg[2:]:
arg += args[i+1]
i += 1
compile_opts.append(arg)
if argkey in ('-std', '-mmacosx-version-min'):
compile_opts.append(arg)
# Special case debug options to only pass -g to clang. This is
# wrong.
if arg in ('-g', '-gdwarf-2'):
compile_opts.append('-g')
# Options with one argument that should pass through to compiler
if arg in [ '-include', '-idirafter', '-iprefix',
'-iquote', '-isystem', '-iwithprefix',
'-iwithprefixbefore']:
compile_opts.append(arg)
compile_opts.append(args[i+1])
i += 1
# Options with no arguments that should pass through
if (arg in ('-dynamiclib', '-bundle', '-headerpad_max_install_names',
'-nostdlib', '-static', '-dynamic', '-r') or
arg.startswith('-Wl,')):
link_opts.append(arg)
# Options with one argument that should pass through
if arg in ('-framework', '-multiply_defined', '-bundle_loader',
'-weak_framework',
'-e', '-install_name',
'-unexported_symbols_list', '-exported_symbols_list',
'-compatibility_version', '-current_version', '-init',
'-seg1addr', '-dylib_file', '-Xlinker'):
link_opts.append(arg)
link_opts.append(args[i+1])
i += 1
# Options with one argument that should pass through to both
if arg in ['-isysroot', '-arch']:
compile_opts.append(arg)
compile_opts.append(args[i+1])
link_opts.append(arg)
link_opts.append(args[i+1])
i += 1
# Options with three arguments that should pass through
if arg in ('-sectorder',):
link_opts.extend(args[i:i+4])
i += 3
# Prefix matches for the link mode
if arg[:2] in ['-l', '-L', '-F', '-R']:
link_opts.append(arg)
# Enable threads
if arg == '-pthread':
link_opts.append('-lpthread')
# Input files
if arg == '-filelist':
f = open(args[i+1])
for line in f:
files.append(line.strip())
f.close()
i += 1
if arg == '-x':
language = args[i+1]
i += 1
if arg[0] != '-':
files.append(arg)
# Output file
if arg == '-o':
output = args[i+1]
i += 1
i += 1
if action == 'print-prog-name':
# assume we can handle everything
print sys.argv[0]
return
if not files:
error('no input files')
if action == 'preprocess' or save_temps:
for i, file in enumerate(files):
if not language:
language = inferlanguage(extension(file))
if save_temps and action != 'preprocess':
# Need a temporary output file
if language == 'c':
poutput = changeextension(file, "i");
elif language == 'objective-c':
poutput = changeextension(file, "mi");
else:
poutput = changeextension(file, "tmp." + extension(file))
files[i] = poutput
else:
poutput = output
args = []
if language:
args.extend(['-x', language])
if poutput:
args += ['-o', poutput, file] + compile_opts
else:
args += [file] + compile_opts
preprocess(args)
# Discard the explicit language after used once
language = ''
if action == 'syntax-only':
for i, file in enumerate(files):
if not language:
language = inferlanguage(extension(file))
args = []
if language:
args.extend(['-x', language])
args += [file] + compile_opts
syntaxonly(args)
language = ''
if action == 'compile' or save_temps:
for i, file in enumerate(files):
if not language:
language = inferlanguage(extension(file))
if save_temps and action != "compile":
# Need a temporary output file
coutput = changeextension(file, "o");
files[i] = coutput
elif not output:
coutput = changeextension(file, "o")
else:
coutput = output
args = []
if language:
args.extend(['-x', language])
args += ['-o', coutput, file] + compile_opts
checked_compile(args, native, language, save_temps)
language = ''
if action == 'link':
for i, file in enumerate(files):
if not language:
language = inferlanguage(extension(file))
ext = extension(file)
if ext != "o" and ext != "a" and ext != "so":
out = changeextension(file, "o")
args = []
if language:
args.extend(['-x', language])
args = ['-o', out, file] + compile_opts
checked_compile(args, native, language, save_temps)
language = ''
files[i] = out
if not output:
output = 'a.out'
args = ['-o', output] + files + link_opts
link(args, native)
if __name__ == '__main__':
main(sys.argv[1:])