Skip to content

Commit e2f5a7b

Browse files
committed
Merge pull request #4 from delcypher/master
Various Improvements
2 parents e4bd86b + e3cb283 commit e2f5a7b

File tree

8 files changed

+419
-53
lines changed

8 files changed

+419
-53
lines changed

README.md

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,12 @@ bitcode file in an ELF section of the actual object file.
1010

1111
When object files are linked together, the contents of non-special ELF
1212
sections are just concatenated (so we don't lose the locations of any
13-
of the constituent bitcode files). This package contains an extra
14-
utility, extract-bc, to read the contents of this ELF section and link
15-
all of the bitcode into a single whole-program bitcode file.
13+
of the constituent bitcode files).
14+
15+
This package contains an extra utility, extract-bc, to read the
16+
contents of this ELF section and link all of the bitcode into a single
17+
whole-program bitcode file. This utility can also be used on built
18+
native static libraries to generate LLVM bitcode archives.
1619

1720
This two-phase build process is slower and more elaborate than normal,
1821
but in practice is necessary to be a drop-in replacement for gcc in
@@ -41,8 +44,18 @@ wrapper script:
4144
Once the environment is set up, just use wllvm and wllvm++ as your C
4245
and C++ compilers, respectively.
4346

44-
Example
45-
=======
47+
In addition to the above environment variables the following can be optionally used:
48+
49+
* `LLVM_COMPILER_PATH` can be set to the absolute path to the folder that
50+
contains the compiler and other LLVM tools such as `llvm-link` to be used.
51+
This prevents searching for the compiler in your PATH environment variable.
52+
This can be useful if you have different versions of clang on your system
53+
and you want to easily switch compilers without tinkering with your PATH
54+
variable.
55+
Example `LLVM_COMPILER_PATH=/home/user/llvm_and_clang/Debug+Asserts/bin`.
56+
57+
Example building bitcode module
58+
===============================
4659

4760
export LLVM_COMPILER=dragonegg
4861
export LLVM_GCC_PREFIX=llvm-
@@ -56,3 +69,32 @@ Example
5669
# Produces pkg-config.bc
5770
extract-bc pkg-config
5871

72+
Example building bitcode archive
73+
================================
74+
75+
export LLVM_COMPILER=clang
76+
tar -xvf bullet-2.81-rev2613.tgz
77+
mkdir bullet-bin
78+
cd bullet-bin
79+
CC=wllvm CXX=wllvm++ cmake ../bullet-2.81-rev2613/
80+
make
81+
82+
# Produces src/LinearMath/libLinearMath.bca
83+
extract-bc src/LinearMath/libLinearMath.a
84+
85+
Debugging
86+
=========
87+
88+
The WLLVM tools can show various levels of output to aid with debugging.
89+
To show this output set WLLVM_OUTPUT to one of the following levels:
90+
91+
* `CRITICAL`
92+
* `ERROR`
93+
* `WARNING`
94+
* `INFO`
95+
* `DEBUG`
96+
97+
For example
98+
99+
export WLLVM_OUTPUT=DEBUG
100+

driver/as

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@
1111
# compiler). We'll link this together at a later stage.
1212

1313
import os
14-
import subprocess, sys
14+
import sys
1515
from utils import *
16+
from subprocess import *
17+
from popenwrapper import Popen
1618

1719

1820
class BCFilter(ArgumentListFilter):

driver/logconfig.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
"""
2+
This module is intended to be imported by command line tools so they can
3+
configure the root logger so that other loggers used in other modules can
4+
inherit the configuration.
5+
"""
6+
import logging
7+
import os
8+
import sys
9+
10+
_loggingEnv = 'WLLVM_OUTPUT'
11+
_validLogLevels = ['CRITICAL','ERROR', 'WARNING', 'INFO', 'DEBUG']
12+
logging.basicConfig(level=logging.WARNING, format='%(levelname)s:%(message)s')
13+
if os.getenv(_loggingEnv):
14+
level = os.getenv(_loggingEnv).upper()
15+
if not level in _validLogLevels:
16+
logging.error('"{0}" is not a valid value for {1} . Valid values are {2}'.format(
17+
level, _loggingEnv, _validLogLevels))
18+
sys.exit(1)
19+
else:
20+
logging.getLogger().setLevel(getattr(logging, level))
21+
22+
# Adjust the format if debugging
23+
if logging.getLogger().getEffectiveLevel() == logging.DEBUG:
24+
formatter = logging.Formatter('%(levelname)s::%(module)s.%(funcName)s() at %(filename)s:%(lineno)d ::%(message)s')
25+
for h in logging.getLogger().handlers:
26+
h.setFormatter(formatter)
27+

driver/popenwrapper.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import os
2+
import subprocess
3+
import pprint
4+
import logging
5+
6+
# This module provides a wrapper for subprocess.POpen
7+
# that can be used for debugging
8+
9+
# Internal logger
10+
_logger = logging.getLogger(__name__)
11+
12+
def Popen(*pargs, **kwargs):
13+
_logger.debug("WLLVM Executing:\n" + pprint.pformat(pargs[0]))
14+
try:
15+
return subprocess.Popen(*pargs,**kwargs)
16+
except OSError:
17+
_logger.error("WWLVM Failed to execute:\n" + pprint.pformat(pargs[0]))
18+
raise

driver/utils.py

Lines changed: 97 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,27 @@
11
from subprocess import *
22
import collections
3+
import pprint
4+
import logging
35
import errno
46
import os
57
import re
68
import sys
79
import tempfile
8-
import subprocess
10+
from driver.popenwrapper import Popen
911

1012
fullSelfPath = os.path.realpath(__file__)
1113
prefix = os.path.dirname(fullSelfPath)
1214
driverDir = prefix
1315

16+
# Environmental variable for path to compiler tools (clang/llvm-link etc..)
17+
llvmCompilerPathEnv = 'LLVM_COMPILER_PATH'
18+
19+
# This is the ELF section name inserted into binaries
20+
elfSectionName='.llvm_bc'
21+
22+
# Internal logger
23+
_logger = logging.getLogger(__name__)
24+
1425
# This class applies filters to GCC argument lists. It has a few
1526
# default arguments that it records, but does not modify the argument
1627
# list at all. It can be subclassed to change this behavior.
@@ -106,7 +117,7 @@ def __init__(self, inputList, exactMatches={}, patternMatches={}):
106117
handler(self, currentItem, *flagArgs)
107118
else:
108119
matched = False
109-
for pattern, (arity, handler) in argPatterns.iteritems():
120+
for pattern, (arity, handler) in argPatterns.items():
110121
if re.match(pattern, currentItem):
111122
flagArgs = self._shiftArgs(arity)
112123
handler(self, currentItem, *flagArgs)
@@ -180,6 +191,39 @@ def __init__(self, arglist):
180191
def outputFileCallback(self, flag, filename):
181192
self.outputFilename = filename
182193

194+
195+
# Static class that allows the type of a file to be checked.
196+
class FileType(object):
197+
# Provides int -> str map
198+
revMap = { }
199+
200+
@classmethod
201+
def getFileType(cls, fileName):
202+
# This is a hacky way of determining
203+
# the type of file we are looking at.
204+
# Maybe we should use python-magic instead?
205+
206+
fileP = Popen(['file',fileName], stdout=PIPE)
207+
output = fileP.communicate()[0]
208+
output = output.decode()
209+
if 'ELF' in output and 'executable' in output:
210+
return cls.EXECUTABLE
211+
elif 'current ar archive' in output:
212+
return cls.ARCHIVE
213+
elif 'ELF' in output and 'relocatable' in output:
214+
return cls.OBJECT
215+
else:
216+
return cls.UNKNOWN
217+
218+
@classmethod
219+
def init(cls):
220+
for (index, name) in enumerate(('UNKNOWN', 'EXECUTABLE', 'OBJECT', 'ARCHIVE')):
221+
setattr(cls, name, index)
222+
cls.revMap[index] = name
223+
224+
# Initialise FileType static class
225+
FileType.init()
226+
183227
def attachBitcodePathToObject(bcPath, outFileName):
184228
# Don't try to attach a bitcode path to a binary. Unfortunately
185229
# that won't work.
@@ -189,9 +233,11 @@ def attachBitcodePathToObject(bcPath, outFileName):
189233

190234
# Now just build a temporary text file with the full path to the
191235
# bitcode file that we'll write into the object file.
192-
f = tempfile.NamedTemporaryFile(mode='rw+b', delete=False)
193-
f.write(os.path.abspath(bcPath))
194-
f.write('\n')
236+
f = tempfile.NamedTemporaryFile(mode='w+b', delete=False)
237+
absBcPath = os.path.abspath(bcPath)
238+
f.write(absBcPath.encode())
239+
f.write('\n'.encode())
240+
_logger.debug(pprint.pformat('Wrote "{0}" to file "{1}"'.format(absBcPath, f.name)))
195241

196242
# Ensure buffers are flushed so that objcopy doesn't read an empty
197243
# file
@@ -200,12 +246,12 @@ def attachBitcodePathToObject(bcPath, outFileName):
200246
f.close()
201247

202248
# Now write our .llvm_bc section
203-
objcopyCmd = ['objcopy', '--add-section', '.llvm_bc={0}'.format(f.name), outFileName]
249+
objcopyCmd = ['objcopy', '--add-section', '{0}={1}'.format(elfSectionName, f.name), outFileName]
204250
orc = 0
205251

206252
try:
207253
if os.path.getsize(outFileName) > 0:
208-
objProc = subprocess.Popen(objcopyCmd)
254+
objProc = Popen(objcopyCmd)
209255
orc = objProc.wait()
210256
except OSError:
211257
# configure loves to immediately delete things, causing issues for
@@ -216,28 +262,44 @@ def attachBitcodePathToObject(bcPath, outFileName):
216262
os.remove(f.name)
217263

218264
if orc != 0:
219-
print objcopyCmd
220-
print('objcopy failed with {0}'.format(orc))
265+
_logger.error('objcopy failed with {0}'.format(orc))
221266
sys.exit(-1)
222267

223268
class BuilderBase(object):
224-
def __init__(self, cmd, isCxx):
269+
def __init__(self, cmd, isCxx, prefixPath=None):
225270
self.cmd = cmd
226271
self.isCxx = isCxx
227272

273+
# Used as prefix path for compiler
274+
if prefixPath:
275+
self.prefixPath = prefixPath
276+
277+
# Ensure prefixPath has trailing slash
278+
if self.prefixPath[-1] != os.path.sep:
279+
self.prefixPath = self.prefixPath + os.path.sep
280+
281+
# Check prefix path exists
282+
if not os.path.exists(self.prefixPath):
283+
errorMsg='Path to compiler "{0}" does not exist'.format(self.prefixPath)
284+
_logger.error(errorMsg)
285+
raise Exception(errorMsg)
286+
287+
else:
288+
self.prefixPath = ''
289+
228290
class ClangBuilder(BuilderBase):
229-
def __init__(self, cmd, isCxx):
230-
super(ClangBuilder, self).__init__(cmd, isCxx)
291+
def __init__(self, cmd, isCxx, prefixPath=None):
292+
super(ClangBuilder, self).__init__(cmd, isCxx, prefixPath)
231293

232294
def getBitcodeCompiler(self):
233295
cc = self.getCompiler()
234296
return cc + ['-emit-llvm']
235297

236298
def getCompiler(self):
237299
if self.isCxx:
238-
return ['clang++']
300+
return ['{0}clang++'.format(self.prefixPath)]
239301
else:
240-
return ['clang']
302+
return ['{0}clang'.format(self.prefixPath)]
241303

242304
def getBitcodeArglistFilter(self):
243305
return ClangBitcodeArgumentListFilter(self.cmd)
@@ -259,8 +321,8 @@ def attachBitcode(self, argFilter):
259321
attachBitcodePathToObject(bcname, outFile)
260322

261323
class DragoneggBuilder(BuilderBase):
262-
def __init__(self, cmd, isCxx):
263-
super(DragoneggBuilder, self).__init__(cmd, isCxx)
324+
def __init__(self, cmd, isCxx, prefixPath=None):
325+
super(DragoneggBuilder, self).__init__(cmd, isCxx, prefixPath)
264326

265327
def getBitcodeCompiler(self):
266328
pth = os.getenv('LLVM_DRAGONEGG_PLUGIN')
@@ -274,9 +336,9 @@ def getCompiler(self):
274336
pfx = os.getenv('LLVM_GCC_PREFIX')
275337

276338
if self.isCxx:
277-
return ['{0}g++'.format(pfx)]
339+
return ['{0}{1}g++'.format(self.prefixPath, pfx)]
278340
else:
279-
return ['{0}gcc'.format(pfx)]
341+
return ['{0}{1}gcc'.format(self.prefixPath, pfx)]
280342

281343
def getBitcodeArglistFilter(self):
282344
return ArgumentListFilter(self.cmd)
@@ -291,13 +353,25 @@ def extraBitcodeArgs(self, argFilter):
291353

292354

293355
def getBuilder(cmd, isCxx):
294-
cstring = os.getenv('LLVM_COMPILER')
356+
compilerEnv = 'LLVM_COMPILER'
357+
cstring = os.getenv(compilerEnv)
358+
pathPrefix = os.getenv(llvmCompilerPathEnv) # Optional
359+
_logger.info('WLLVM compiler using {0}'.format(cstring))
360+
if pathPrefix:
361+
_logger.info('WLLVM compiler path prefix "{0}"'.format(pathPrefix))
362+
295363
if cstring == 'clang':
296-
return ClangBuilder(cmd, isCxx)
364+
return ClangBuilder(cmd, isCxx, pathPrefix)
297365
elif cstring == 'dragonegg':
298-
return DragoneggBuilder(cmd, isCxx)
366+
return DragoneggBuilder(cmd, isCxx, pathPrefix)
367+
elif cstring == None:
368+
errorMsg = ' No compiler set. Please set environment variable ' + compilerEnv
369+
_logger.critical(errorMsg)
370+
raise Exception(errorMsg)
299371
else:
300-
raise Exception('Invalid compiler type: ' + cstring)
372+
errorMsg= compilerEnv + '=' + str(cstring) + ' : Invalid compiler type'
373+
_logger.critical(errorMsg)
374+
raise Exception(errorMsg)
301375

302376
def buildObject(builder):
303377
objCompiler = builder.getCompiler()
@@ -330,3 +404,4 @@ def buildAndAttachBitcode(builder):
330404
if rc == 0:
331405
builder.attachBitcode(af)
332406
sys.exit(rc)
407+

0 commit comments

Comments
 (0)