rizsotto.mailinglist updated this revision to Diff 39703.
rizsotto.mailinglist added a comment.

findings from compare from older Perl implementation


http://reviews.llvm.org/D9600

Files:
  tools/scan-build-py/README.md
  tools/scan-build-py/libear/CMakeLists.txt
  tools/scan-build-py/libear/__init__.py
  tools/scan-build-py/libear/config.h.in
  tools/scan-build-py/libear/ear.c
  tools/scan-build-py/libscanbuild/clang.py
  tools/scan-build-py/libscanbuild/command.py
  tools/scan-build-py/libscanbuild/driver.py
  tools/scan-build-py/libscanbuild/intercept.py
  tools/scan-build-py/libscanbuild/interposition.py
  tools/scan-build-py/libscanbuild/options.py
  tools/scan-build-py/libscanbuild/report.py
  tools/scan-build-py/libscanbuild/runner.py
  tools/scan-build-py/libscanbuild/shell.py
  tools/scan-build-py/setup.py
  tools/scan-build-py/tests/functional/cases/__init__.py
  tools/scan-build-py/tests/functional/cases/test_from_cdb.py
  tools/scan-build-py/tests/functional/cases/test_from_cmd.py
  tools/scan-build-py/tests/functional/exec/main.c
  tools/scan-build-py/tests/functional/src/build/Makefile
  tools/scan-build-py/tests/unit/__init__.py
  tools/scan-build-py/tests/unit/test_clang.py
  tools/scan-build-py/tests/unit/test_command.py
  tools/scan-build-py/tests/unit/test_intercept.py
  tools/scan-build-py/tests/unit/test_runner.py
  tools/scan-build-py/tests/unit/test_shell.py

Index: tools/scan-build-py/tests/unit/test_shell.py
===================================================================
--- /dev/null
+++ tools/scan-build-py/tests/unit/test_shell.py
@@ -0,0 +1,42 @@
+# -*- coding: utf-8 -*-
+#                     The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+
+import libscanbuild.shell as sut
+import unittest
+
+
+class ShellTest(unittest.TestCase):
+
+    def test_encode_decode_are_same(self):
+        def test(value):
+            self.assertEqual(sut.encode(sut.decode(value)), value)
+
+        test("")
+        test("clang")
+        test("clang this and that")
+
+    def test_decode_encode_are_same(self):
+        def test(value):
+            self.assertEqual(sut.decode(sut.encode(value)), value)
+
+        test([])
+        test(['clang'])
+        test(['clang', 'this', 'and', 'that'])
+        test(['clang', 'this and', 'that'])
+        test(['clang', "it's me", 'again'])
+        test(['clang', 'some "words" are', 'quoted'])
+
+    def test_encode(self):
+        self.assertEqual(sut.encode(['clang', "it's me", 'again']),
+                         'clang "it\'s me" again')
+        self.assertEqual(sut.encode(['clang', "it(s me", 'again)']),
+                         'clang "it(s me" "again)"')
+        self.assertEqual(sut.encode(['clang', 'redirect > it']),
+                         'clang "redirect > it"')
+        self.assertEqual(sut.encode(['clang', '-DKEY="VALUE"']),
+                         'clang -DKEY=\\"VALUE\\"')
+        self.assertEqual(sut.encode(['clang', '-DKEY="value with spaces"']),
+                         'clang -DKEY=\\"value with spaces\\"')
Index: tools/scan-build-py/tests/unit/test_runner.py
===================================================================
--- tools/scan-build-py/tests/unit/test_runner.py
+++ tools/scan-build-py/tests/unit/test_runner.py
@@ -141,18 +141,19 @@
         def test(expected, input):
             spy = fixtures.Spy()
             self.assertEqual(spy.success, sut.language_check(input, spy.call))
-            self.assertEqual(expected, spy.arg)
+            self.assertEqual(expected, spy.arg['language'])
 
         l = 'language'
         f = 'file'
-        i = 'cxx'
-        test({f: 'file.c', l: 'c'}, {f: 'file.c', l: 'c'})
-        test({f: 'file.c', l: 'c++'}, {f: 'file.c', l: 'c++'})
-        test({f: 'file.c', l: 'c++', i: True}, {f: 'file.c', i: True})
-        test({f: 'file.c', l: 'c'}, {f: 'file.c'})
-        test({f: 'file.cxx', l: 'c++'}, {f: 'file.cxx'})
-        test({f: 'file.i', l: 'c-cpp-output'}, {f: 'file.i'})
-        test({f: 'f.i', l: 'c-cpp-output'}, {f: 'f.i', l: 'c-cpp-output'})
+        i = 'c++'
+        test('c',   {f: 'file.c', l: 'c', i: False})
+        test('c++', {f: 'file.c', l: 'c++', i: False})
+        test('c++', {f: 'file.c', i: True})
+        test('c',   {f: 'file.c', i: False})
+        test('c++', {f: 'file.cxx', i: False})
+        test('c-cpp-output',   {f: 'file.i', i: False})
+        test('c++-cpp-output', {f: 'file.i', i: True})
+        test('c-cpp-output',   {f: 'f.i', l: 'c-cpp-output', i: True})
 
     def test_arch_loop(self):
         def test(input):
@@ -163,16 +164,16 @@
         input = {'key': 'value'}
         self.assertEqual(input, test(input))
 
-        input = {'archs_seen': ['-arch', 'i386']}
+        input = {'archs_seen': ['i386']}
         self.assertEqual({'arch': 'i386'}, test(input))
 
-        input = {'archs_seen': ['-arch', 'ppc']}
+        input = {'archs_seen': ['ppc']}
         self.assertEqual(None, test(input))
 
-        input = {'archs_seen': ['-arch', 'i386', '-arch', 'ppc']}
+        input = {'archs_seen': ['i386', 'ppc']}
         self.assertEqual({'arch': 'i386'}, test(input))
 
-        input = {'archs_seen': ['-arch', 'i386', '-arch', 'sparc']}
+        input = {'archs_seen': ['i386', 'sparc']}
         result = test(input)
         self.assertTrue(result == {'arch': 'i386'} or
                         result == {'arch': 'sparc'})
Index: tools/scan-build-py/tests/unit/test_intercept.py
===================================================================
--- tools/scan-build-py/tests/unit/test_intercept.py
+++ tools/scan-build-py/tests/unit/test_intercept.py
@@ -13,7 +13,7 @@
 
     def test_compiler_call_filter(self):
         def test(command):
-            return sut.is_compiler_call({'command': [command]})
+            return sut.compiler_call({'command': [command]})
 
         self.assertTrue(test('clang'))
         self.assertTrue(test('clang-3.6'))
Index: tools/scan-build-py/tests/unit/test_command.py
===================================================================
--- tools/scan-build-py/tests/unit/test_command.py
+++ tools/scan-build-py/tests/unit/test_command.py
@@ -16,9 +16,6 @@
             opts = sut.classify_parameters(cmd)
             self.assertEqual(expected, opts['action'])
 
-        Info = sut.Action.Info
-        test(Info, ['clang', 'source.c', '-print-prog-name'])
-
         Link = sut.Action.Link
         test(Link, ['clang', 'source.c'])
 
@@ -26,7 +23,7 @@
         test(Compile, ['clang', '-c', 'source.c'])
         test(Compile, ['clang', '-c', 'source.c', '-MF', 'source.d'])
 
-        Preprocess = sut.Action.Preprocess
+        Preprocess = sut.Action.Ignored
         test(Preprocess, ['clang', '-E', 'source.c'])
         test(Preprocess, ['clang', '-c', '-E', 'source.c'])
         test(Preprocess, ['clang', '-c', '-M', 'source.c'])
@@ -37,9 +34,9 @@
             opts = sut.classify_parameters(cmd)
             return opts.get('compile_options', [])
 
-        self.assertEqual(['-O1'], test(['clang', '-c', 'source.c', '-O']))
+        self.assertEqual(['-O'],  test(['clang', '-c', 'source.c', '-O']))
         self.assertEqual(['-O1'], test(['clang', '-c', 'source.c', '-O1']))
-        self.assertEqual(['-O2'], test(['clang', '-c', 'source.c', '-Os']))
+        self.assertEqual(['-Os'], test(['clang', '-c', 'source.c', '-Os']))
         self.assertEqual(['-O2'], test(['clang', '-c', 'source.c', '-O2']))
         self.assertEqual(['-O3'], test(['clang', '-c', 'source.c', '-O3']))
 
@@ -52,6 +49,15 @@
         self.assertEqual('c', test(['clang', '-c', 'source.c', '-x', 'c']))
         self.assertEqual('cpp', test(['clang', '-c', 'source.c', '-x', 'cpp']))
 
+    def test_output(self):
+        def test(cmd):
+            opts = sut.classify_parameters(cmd)
+            return opts.get('output')
+
+        self.assertEqual(None, test(['clang', '-c', 'source.c']))
+        self.assertEqual('source.o',
+                         test(['clang', '-c', '-o', 'source.o', 'source.c']))
+
     def test_arch(self):
         def test(cmd):
             opts = sut.classify_parameters(cmd)
@@ -60,9 +66,9 @@
         eq = self.assertEqual
 
         eq([], test(['clang', '-c', 'source.c']))
-        eq(['-arch', 'mips'],
+        eq(['mips'],
            test(['clang', '-c', 'source.c', '-arch', 'mips']))
-        eq(['-arch', 'mips', '-arch', 'i386'],
+        eq(['mips', 'i386'],
            test(['clang', '-c', 'source.c', '-arch', 'mips', '-arch', 'i386']))
 
     def test_input_file(self):
@@ -76,17 +82,6 @@
         eq(['src.c'], test(['clang', '-c', 'src.c']))
         eq(['s1.c', 's2.c'], test(['clang', '-c', 's1.c', 's2.c']))
 
-    def test_output_file(self):
-        def test(cmd):
-            opts = sut.classify_parameters(cmd)
-            return opts.get('output', None)
-
-        eq = self.assertEqual
-
-        eq(None, test(['clang', 'src.c']))
-        eq('src.o', test(['clang', '-c', 'src.c', '-o', 'src.o']))
-        eq('src.o', test(['clang', '-c', '-o', 'src.o', 'src.c']))
-
     def test_include(self):
         def test(cmd):
             opts = sut.classify_parameters(cmd)
@@ -130,20 +125,16 @@
            test(['clang', '-c', 'src.c', '-Dvar="val ues"']))
 
     def test_ignored_flags(self):
-        def test(cmd):
-            salt = ['-I.', '-D_THIS']
-            opts = sut.classify_parameters(cmd + salt)
-            self.assertEqual(salt, opts.get('compile_options'))
-            return opts.get('link_options', [])
+        def test(flags):
+            cmd = ['clang', 'src.o']
+            opts = sut.classify_parameters(cmd + flags)
+            self.assertEqual(['src.o'], opts.get('compile_options'))
 
-        eq = self.assertEqual
-
-        eq([],
-           test(['clang', 'src.o']))
-        eq([],
-           test(['clang', 'src.o', '-lrt', '-L/opt/company/lib']))
-        eq([],
-           test(['clang', 'src.o', '-framework', 'foo']))
+        test([])
+        test(['-lrt', '-L/opt/company/lib'])
+        test(['-static'])
+        test(['-Wnoexcept', '-Wall'])
+        test(['-mtune=i386', '-mcpu=i386'])
 
     def test_compile_only_flags(self):
         def test(cmd):
@@ -152,17 +143,8 @@
 
         eq = self.assertEqual
 
-        eq([], test(['clang', '-c', 'src.c']))
-        eq([],
-           test(['clang', '-c', 'src.c', '-Wnoexcept']))
-        eq([],
-           test(['clang', '-c', 'src.c', '-Wall']))
-        eq(['-Wno-cpp'],
-           test(['clang', '-c', 'src.c', '-Wno-cpp']))
         eq(['-std=C99'],
            test(['clang', '-c', 'src.c', '-std=C99']))
-        eq(['-mtune=i386', '-mcpu=i386'],
-           test(['clang', '-c', 'src.c', '-mtune=i386', '-mcpu=i386']))
         eq(['-nostdinc'],
            test(['clang', '-c', 'src.c', '-nostdinc']))
         eq(['-isystem', '/image/debian'],
@@ -181,8 +163,6 @@
 
         eq = self.assertEqual
 
-        eq([],
-           test(['clang', '-c', 'src.c', '-fsyntax-only']))
         eq(['-fsinged-char'],
            test(['clang', '-c', 'src.c', '-fsinged-char']))
         eq(['-fPIC'],
@@ -194,12 +174,14 @@
         eq(['-isysroot', '/'],
            test(['clang', '-c', 'src.c', '-isysroot', '/']))
         eq([],
+           test(['clang', '-c', 'src.c', '-fsyntax-only']))
+        eq([],
            test(['clang', '-c', 'src.c', '-sectorder', 'a', 'b', 'c']))
 
     def test_detect_cxx_from_compiler_name(self):
         def test(cmd):
             opts = sut.classify_parameters(cmd)
-            return opts.get('cxx')
+            return opts.get('c++')
 
         eq = self.assertEqual
 
Index: tools/scan-build-py/tests/unit/test_clang.py
===================================================================
--- tools/scan-build-py/tests/unit/test_clang.py
+++ tools/scan-build-py/tests/unit/test_clang.py
@@ -18,8 +18,8 @@
                 handle.write('')
 
             result = sut.get_arguments(
-                tmpdir,
-                ['clang', '-c', filename, '-DNDEBUG', '-Dvar="this is it"'])
+                ['clang', '-c', filename, '-DNDEBUG', '-Dvar="this is it"'],
+                tmpdir)
 
             self.assertIn('NDEBUG', result)
             self.assertIn('var="this is it"', result)
@@ -28,5 +28,5 @@
         self.assertRaises(
             Exception,
             sut.get_arguments,
-            '.',
-            ['clang', '-###', '-fsyntax-only', '-x', 'c', 'notexist.c'])
+            ['clang', '-###', '-fsyntax-only', '-x', 'c', 'notexist.c'],
+            '.')
Index: tools/scan-build-py/tests/unit/__init__.py
===================================================================
--- tools/scan-build-py/tests/unit/__init__.py
+++ tools/scan-build-py/tests/unit/__init__.py
@@ -10,6 +10,7 @@
 from . import test_report
 from . import test_driver
 from . import test_intercept
+from . import test_shell
 
 
 def load_tests(loader, suite, pattern):
@@ -19,4 +20,5 @@
     suite.addTests(loader.loadTestsFromModule(test_report))
     suite.addTests(loader.loadTestsFromModule(test_driver))
     suite.addTests(loader.loadTestsFromModule(test_intercept))
+    suite.addTests(loader.loadTestsFromModule(test_shell))
     return suite
Index: tools/scan-build-py/tests/functional/src/build/Makefile
===================================================================
--- tools/scan-build-py/tests/functional/src/build/Makefile
+++ tools/scan-build-py/tests/functional/src/build/Makefile
@@ -31,3 +31,6 @@
 
 build_all_in_one:
 	$(CC) -o $(PROGRAM) $(CFLAGS) $(LDFLAGS) $(CLEAN_SRCS:%.c=$(SRCDIR)/%.c)
+
+clean:
+	rm -f $(PROGRAM) $(OBJDIR)/*.o
Index: tools/scan-build-py/tests/functional/exec/main.c
===================================================================
--- tools/scan-build-py/tests/functional/exec/main.c
+++ tools/scan-build-py/tests/functional/exec/main.c
@@ -58,7 +58,7 @@
     cwd = NULL;
 }
 
-void expected_out(const char *cmd, const char *file) {
+void expected_out(const char *file) {
     if (need_comma)
         fprintf(fd, ",\n");
     else
@@ -66,7 +66,7 @@
 
     fprintf(fd, "{\n");
     fprintf(fd, "  \"directory\": \"%s\",\n", cwd);
-    fprintf(fd, "  \"command\": \"%s -c %s\",\n", cmd, file);
+    fprintf(fd, "  \"command\": \"cc -c %s\",\n", file);
     fprintf(fd, "  \"file\": \"%s/%s\"\n", cwd, file);
     fprintf(fd, "}\n");
 }
@@ -116,7 +116,7 @@
     char *const compiler = "/usr/bin/cc";
     char *const argv[] = {"cc", "-c", file, 0};
 
-    expected_out("cc", file);
+    expected_out(file);
     create_source(file);
 
     FORK(execv(compiler, argv);)
@@ -130,7 +130,7 @@
     char *const argv[] = {compiler, "-c", file, 0};
     char *const envp[] = {"THIS=THAT", 0};
 
-    expected_out("/usr/bin/cc", file);
+    expected_out(file);
     create_source(file);
 
     FORK(execve(compiler, argv, envp);)
@@ -143,7 +143,7 @@
     char *const compiler = "cc";
     char *const argv[] = {compiler, "-c", file, 0};
 
-    expected_out(compiler, file);
+    expected_out(file);
     create_source(file);
 
     FORK(execvp(compiler, argv);)
@@ -156,7 +156,7 @@
     char *const compiler = "cc";
     char *const argv[] = {compiler, "-c", file, 0};
 
-    expected_out(compiler, file);
+    expected_out(file);
     create_source(file);
 
     FORK(execvP(compiler, _PATH_DEFPATH, argv);)
@@ -170,7 +170,7 @@
     char *const argv[] = {"/usr/bin/cc", "-c", file, 0};
     char *const envp[] = {"THIS=THAT", 0};
 
-    expected_out("/usr/bin/cc", file);
+    expected_out(file);
     create_source(file);
 
     FORK(execvpe(compiler, argv, envp);)
@@ -182,7 +182,7 @@
     char *const file = "execl.c";
     char *const compiler = "/usr/bin/cc";
 
-    expected_out("cc", file);
+    expected_out(file);
     create_source(file);
 
     FORK(execl(compiler, "cc", "-c", file, (char *)0);)
@@ -194,7 +194,7 @@
     char *const file = "execlp.c";
     char *const compiler = "cc";
 
-    expected_out(compiler, file);
+    expected_out(file);
     create_source(file);
 
     FORK(execlp(compiler, compiler, "-c", file, (char *)0);)
@@ -207,7 +207,7 @@
     char *const compiler = "/usr/bin/cc";
     char *const envp[] = {"THIS=THAT", 0};
 
-    expected_out(compiler, file);
+    expected_out(file);
     create_source(file);
 
     FORK(execle(compiler, compiler, "-c", file, (char *)0, envp);)
@@ -220,7 +220,7 @@
     char *const compiler = "cc";
     char *const argv[] = {compiler, "-c", file, 0};
 
-    expected_out(compiler, file);
+    expected_out(file);
     create_source(file);
 
     pid_t child;
@@ -238,7 +238,7 @@
     char *const compiler = "cc";
     char *const argv[] = {compiler, "-c", file, 0};
 
-    expected_out(compiler, file);
+    expected_out(file);
     create_source(file);
 
     pid_t child;
Index: tools/scan-build-py/tests/functional/cases/test_from_cmd.py
===================================================================
--- tools/scan-build-py/tests/functional/cases/test_from_cmd.py
+++ tools/scan-build-py/tests/functional/cases/test_from_cmd.py
@@ -5,7 +5,7 @@
 # License. See LICENSE.TXT for details.
 
 from ...unit import fixtures
-from . import make_args, silent_check_call
+from . import make_args, check_call_and_report
 import unittest
 
 import os.path
@@ -13,27 +13,25 @@
 
 class OutputDirectoryTest(unittest.TestCase):
     @staticmethod
-    def run_sb(outdir, args):
-        return silent_check_call(
-            ['intercept-build', 'all', '-o', outdir] + args)
+    def run_sb(outdir, args, cmd):
+        return check_call_and_report(
+            ['intercept-build', 'all', '-o', outdir] + args,
+            cmd)
 
     def test_regular_keeps_report_dir(self):
         with fixtures.TempDir() as tmpdir:
-            outdir = os.path.join(tmpdir, 'result')
             make = make_args(tmpdir) + ['build_regular']
-            self.run_sb(outdir, make)
+            outdir = self.run_sb(tmpdir, [], make)
             self.assertTrue(os.path.isdir(outdir))
 
     def test_clear_deletes_report_dir(self):
         with fixtures.TempDir() as tmpdir:
-            outdir = os.path.join(tmpdir, 'result')
             make = make_args(tmpdir) + ['build_clean']
-            self.run_sb(outdir, make)
+            outdir = self.run_sb(tmpdir, [], make)
             self.assertFalse(os.path.isdir(outdir))
 
     def test_clear_keeps_report_dir_when_asked(self):
         with fixtures.TempDir() as tmpdir:
-            outdir = os.path.join(tmpdir, 'result')
             make = make_args(tmpdir) + ['build_clean']
-            self.run_sb(outdir, ['--keep-empty'] + make)
+            outdir = self.run_sb(tmpdir, ['--keep-empty'], make)
             self.assertTrue(os.path.isdir(outdir))
Index: tools/scan-build-py/tests/functional/cases/test_from_cdb.py
===================================================================
--- tools/scan-build-py/tests/functional/cases/test_from_cdb.py
+++ tools/scan-build-py/tests/functional/cases/test_from_cdb.py
@@ -5,6 +5,7 @@
 # License. See LICENSE.TXT for details.
 
 from ...unit import fixtures
+from . import call_and_report
 import unittest
 
 import os.path
@@ -31,82 +32,66 @@
 def run_driver(directory, cdb, args):
     cmd = ['intercept-build', 'analyze', '--cdb', cdb, '--output', directory] \
         + args
-    child = subprocess.Popen(cmd,
-                             universal_newlines=True,
-                             stdout=subprocess.PIPE,
-                             stderr=subprocess.STDOUT)
-    output = child.stdout.readlines()
-    child.stdout.close()
-    child.wait()
-    return (child.returncode, output)
+    return call_and_report(cmd, [])
 
 
 class OutputDirectoryTest(unittest.TestCase):
     def test_regular_keeps_report_dir(self):
         with fixtures.TempDir() as tmpdir:
             cdb = prepare_cdb('regular', tmpdir)
-            outdir = os.path.join(tmpdir, 'result')
-            exit_code, output = run_driver(outdir, cdb, [])
-            self.assertTrue(os.path.isdir(outdir))
+            exit_code, reportdir = run_driver(tmpdir, cdb, [])
+            self.assertTrue(os.path.isdir(reportdir))
 
     def test_clear_deletes_report_dir(self):
         with fixtures.TempDir() as tmpdir:
             cdb = prepare_cdb('clean', tmpdir)
-            outdir = os.path.join(tmpdir, 'result')
-            exit_code, output = run_driver(outdir, cdb, [])
-            self.assertFalse(os.path.isdir(outdir))
+            exit_code, reportdir = run_driver(tmpdir, cdb, [])
+            self.assertFalse(os.path.isdir(reportdir))
 
     def test_clear_keeps_report_dir_when_asked(self):
         with fixtures.TempDir() as tmpdir:
             cdb = prepare_cdb('clean', tmpdir)
-            outdir = os.path.join(tmpdir, 'result')
-            exit_code, output = run_driver(outdir, cdb, ['--keep-empty'])
-            self.assertTrue(os.path.isdir(outdir))
+            exit_code, reportdir = run_driver(tmpdir, cdb, ['--keep-empty'])
+            self.assertTrue(os.path.isdir(reportdir))
 
 
 class ExitCodeTest(unittest.TestCase):
     def test_regular_does_not_set_exit_code(self):
         with fixtures.TempDir() as tmpdir:
             cdb = prepare_cdb('regular', tmpdir)
-            outdir = os.path.join(tmpdir, 'result')
-            exit_code, output = run_driver(outdir, cdb, [])
+            exit_code, __ = run_driver(tmpdir, cdb, [])
             self.assertFalse(exit_code)
 
     def test_clear_does_not_set_exit_code(self):
         with fixtures.TempDir() as tmpdir:
             cdb = prepare_cdb('clean', tmpdir)
-            outdir = os.path.join(tmpdir, 'result')
-            exit_code, output = run_driver(outdir, cdb, [])
+            exit_code, __ = run_driver(tmpdir, cdb, [])
             self.assertFalse(exit_code)
 
     def test_regular_sets_exit_code_if_asked(self):
         with fixtures.TempDir() as tmpdir:
             cdb = prepare_cdb('regular', tmpdir)
-            outdir = os.path.join(tmpdir, 'result')
-            exit_code, output = run_driver(outdir, cdb, ['--status-bugs'])
+            exit_code, __ = run_driver(tmpdir, cdb, ['--status-bugs'])
             self.assertTrue(exit_code)
 
     def test_clear_does_not_set_exit_code_if_asked(self):
         with fixtures.TempDir() as tmpdir:
             cdb = prepare_cdb('clean', tmpdir)
-            outdir = os.path.join(tmpdir, 'result')
-            exit_code, output = run_driver(outdir, cdb, ['--status-bugs'])
+            exit_code, __ = run_driver(tmpdir, cdb, ['--status-bugs'])
             self.assertFalse(exit_code)
 
     def test_regular_sets_exit_code_if_asked_from_plist(self):
         with fixtures.TempDir() as tmpdir:
             cdb = prepare_cdb('regular', tmpdir)
-            outdir = os.path.join(tmpdir, 'result')
-            exit_code, output = run_driver(
-                outdir, cdb, ['--status-bugs', '--plist'])
+            exit_code, __ = run_driver(
+                tmpdir, cdb, ['--status-bugs', '--plist'])
             self.assertTrue(exit_code)
 
     def test_clear_does_not_set_exit_code_if_asked_from_plist(self):
         with fixtures.TempDir() as tmpdir:
             cdb = prepare_cdb('clean', tmpdir)
-            outdir = os.path.join(tmpdir, 'result')
-            exit_code, output = run_driver(
-                outdir, cdb, ['--status-bugs', '--plist'])
+            exit_code, __ = run_driver(
+                tmpdir, cdb, ['--status-bugs', '--plist'])
             self.assertFalse(exit_code)
 
 
@@ -122,47 +107,46 @@
     def test_default_creates_html_report(self):
         with fixtures.TempDir() as tmpdir:
             cdb = prepare_cdb('regular', tmpdir)
-            outdir = os.path.join(tmpdir, 'result')
-            exit_code, output = run_driver(outdir, cdb, [])
-            self.assertTrue(os.path.exists(os.path.join(outdir, 'index.html')))
-            self.assertEqual(self.get_html_count(outdir), 2)
-            self.assertEqual(self.get_plist_count(outdir), 0)
+            exit_code, reportdir = run_driver(tmpdir, cdb, [])
+            self.assertTrue(
+                os.path.exists(os.path.join(reportdir, 'index.html')))
+            self.assertEqual(self.get_html_count(reportdir), 2)
+            self.assertEqual(self.get_plist_count(reportdir), 0)
 
     def test_plist_and_html_creates_html_report(self):
         with fixtures.TempDir() as tmpdir:
             cdb = prepare_cdb('regular', tmpdir)
-            outdir = os.path.join(tmpdir, 'result')
-            exit_code, output = run_driver(outdir, cdb, ['--plist-html'])
-            self.assertTrue(os.path.exists(os.path.join(outdir, 'index.html')))
-            self.assertEqual(self.get_html_count(outdir), 2)
-            self.assertEqual(self.get_plist_count(outdir), 5)
+            exit_code, reportdir = run_driver(tmpdir, cdb, ['--plist-html'])
+            self.assertTrue(
+                os.path.exists(os.path.join(reportdir, 'index.html')))
+            self.assertEqual(self.get_html_count(reportdir), 2)
+            self.assertEqual(self.get_plist_count(reportdir), 5)
 
     def test_plist_does_not_creates_html_report(self):
         with fixtures.TempDir() as tmpdir:
             cdb = prepare_cdb('regular', tmpdir)
-            outdir = os.path.join(tmpdir, 'result')
-            exit_code, output = run_driver(outdir, cdb, ['--plist'])
+            exit_code, reportdir = run_driver(tmpdir, cdb, ['--plist'])
             self.assertFalse(
-                os.path.exists(os.path.join(outdir, 'index.html')))
-            self.assertEqual(self.get_html_count(outdir), 0)
-            self.assertEqual(self.get_plist_count(outdir), 5)
+                os.path.exists(os.path.join(reportdir, 'index.html')))
+            self.assertEqual(self.get_html_count(reportdir), 0)
+            self.assertEqual(self.get_plist_count(reportdir), 5)
 
 
 class FailureReportTest(unittest.TestCase):
     def test_broken_creates_failure_reports(self):
         with fixtures.TempDir() as tmpdir:
             cdb = prepare_cdb('broken', tmpdir)
-            outdir = os.path.join(tmpdir, 'result')
-            exit_code, output = run_driver(outdir, cdb, [])
-            self.assertTrue(os.path.isdir(os.path.join(outdir, 'failures')))
+            exit_code, reportdir = run_driver(tmpdir, cdb, [])
+            self.assertTrue(
+                os.path.isdir(os.path.join(reportdir, 'failures')))
 
     def test_broken_does_not_creates_failure_reports(self):
         with fixtures.TempDir() as tmpdir:
             cdb = prepare_cdb('broken', tmpdir)
-            outdir = os.path.join(tmpdir, 'result')
-            exit_code, output = run_driver(
-                outdir, cdb, ['--no-failure-reports'])
-            self.assertFalse(os.path.isdir(os.path.join(outdir, 'failures')))
+            exit_code, reportdir = run_driver(
+                tmpdir, cdb, ['--no-failure-reports'])
+            self.assertFalse(
+                os.path.isdir(os.path.join(reportdir, 'failures')))
 
 
 class TitleTest(unittest.TestCase):
@@ -174,7 +158,7 @@
         ]
         result = dict()
 
-        index = os.path.join(directory, 'result', 'index.html')
+        index = os.path.join(directory, 'index.html')
         with open(index, 'r') as handler:
             for line in handler.readlines():
                 for regex in patterns:
@@ -188,14 +172,12 @@
     def test_default_title_in_report(self):
         with fixtures.TempDir() as tmpdir:
             cdb = prepare_cdb('broken', tmpdir)
-            outdir = os.path.join(tmpdir, 'result')
-            exit_code, output = run_driver(outdir, cdb, [])
-            self.assertTitleEqual(tmpdir, 'src - analyzer results')
+            exit_code, reportdir = run_driver(tmpdir, cdb, [])
+            self.assertTitleEqual(reportdir, 'src - analyzer results')
 
     def test_given_title_in_report(self):
         with fixtures.TempDir() as tmpdir:
             cdb = prepare_cdb('broken', tmpdir)
-            outdir = os.path.join(tmpdir, 'result')
-            exit_code, output = run_driver(
-                outdir, cdb, ['--html-title', 'this is the title'])
-            self.assertTitleEqual(tmpdir, 'this is the title')
+            exit_code, reportdir = run_driver(
+                tmpdir, cdb, ['--html-title', 'this is the title'])
+            self.assertTitleEqual(reportdir, 'this is the title')
Index: tools/scan-build-py/tests/functional/cases/__init__.py
===================================================================
--- tools/scan-build-py/tests/functional/cases/__init__.py
+++ tools/scan-build-py/tests/functional/cases/__init__.py
@@ -4,6 +4,7 @@
 # This file is distributed under the University of Illinois Open Source
 # License. See LICENSE.TXT for details.
 
+import re
 import os.path
 import subprocess
 
@@ -37,3 +38,32 @@
     return subprocess.check_call(cmd,
                                  stdout=subprocess.PIPE,
                                  stderr=subprocess.STDOUT)
+
+
+def call_and_report(analyzer_cmd, build_cmd):
+    child = subprocess.Popen(analyzer_cmd + ['-v'] + build_cmd,
+                             universal_newlines=True,
+                             stdout=subprocess.PIPE,
+                             stderr=subprocess.STDOUT)
+
+    pattern = re.compile('Report directory created: (.+)')
+    directory = None
+    for line in child.stdout.readlines():
+        match = pattern.search(line)
+        if match and match.lastindex == 1:
+            directory = match.group(1)
+            break
+    child.stdout.close()
+    child.wait()
+
+    return (child.returncode, directory)
+
+
+def check_call_and_report(analyzer_cmd, build_cmd):
+    exit_code, result = call_and_report(analyzer_cmd, build_cmd)
+    if exit_code != 0:
+        raise subprocess.CalledProcessError(
+            "Command '{0}' returned non-zero exit status {1}".format(
+                cmd, exit_code))
+    else:
+        return result
Index: tools/scan-build-py/setup.py
===================================================================
--- tools/scan-build-py/setup.py
+++ tools/scan-build-py/setup.py
@@ -2,44 +2,6 @@
 # -*- coding: utf-8 -*-
 
 from setuptools import setup
-from subprocess import check_call
-from distutils.dir_util import mkpath
-from distutils.command.build import build
-from distutils.command.install import install
-
-
-class BuildEAR(build):
-
-    def run(self):
-        import os
-        import os.path
-
-        mkpath(self.build_temp)
-
-        source_dir = os.path.join(os.getcwd(), 'libear')
-        dest_dir = os.path.abspath(self.build_lib)
-
-        cmd = ['cmake', '-DCMAKE_INSTALL_PREFIX=' + dest_dir, source_dir]
-        check_call(cmd, cwd=self.build_temp)
-
-        cmd = ['make', 'install']
-        check_call(cmd, cwd=self.build_temp)
-
-
-class Build(build):
-
-    def run(self):
-        self.run_command('buildear')
-        build.run(self)
-
-
-class Install(install):
-
-    def run(self):
-        self.run_command('build')
-        self.run_command('install_scripts')
-        install.run(self)
-
 
 setup(
     name='beye',
@@ -55,9 +17,8 @@
     scripts=['bin/scan-build',
              'bin/intercept-build', 'bin/intercept-cc', 'bin/intercept-c++',
              'bin/analyze-build', 'bin/analyze-cc', 'bin/analyze-c++'],
-    packages=['libscanbuild'],
-    package_data={'libscanbuild': ['resources/*']},
-    cmdclass={'buildear': BuildEAR, 'install': Install, 'build': Build},
+    packages=['libscanbuild', 'libear'],
+    package_data={'libscanbuild': ['resources/*'], 'libear': ['config.h.in', 'ear.c']},
     classifiers=[
         "Development Status :: 4 - Beta",
         "License :: OSI Approved :: University of Illinois/NCSA Open Source License",
Index: tools/scan-build-py/libscanbuild/shell.py
===================================================================
--- /dev/null
+++ tools/scan-build-py/libscanbuild/shell.py
@@ -0,0 +1,66 @@
+# -*- coding: utf-8 -*-
+#                     The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+""" This module implements basic shell escaping/unescaping methods. """
+
+import re
+import shlex
+
+__all__ = ['encode', 'decode']
+
+
+def encode(command):
+    """ Takes a command as list and returns a string. """
+
+    def needs_quote(arg):
+        """ Returns true if arguments needs to be protected by quotes.
+
+        Previous implementation was shlex.split method, but that's not good
+        for this job. Currently is running through the string with a basic
+        state checking. """
+
+        reserved = {' ', '$', '%', '&', '(', ')', '[', ']', '{', '}', '*', '|',
+                    '<', '>', '@', '?', '!'}
+        state = 0
+        for c in arg:
+            if state == 0 and c in reserved:
+                return True
+            elif state == 0 and c == '\\':
+                state = 1
+            elif state == 1 and c in reserved | {'\\'}:
+                state = 0
+            elif state == 0 and c == '"':
+                state = 2
+            elif state == 2 and c == '"':
+                state = 0
+            elif state == 0 and c == "'":
+                state = 3
+            elif state == 3 and c == "'":
+                state = 0
+        return state != 0
+
+    def escape(arg):
+        """ Do protect argument if that's needed. """
+
+        table = {'\\': '\\\\', '"': '\\"'}
+        escaped = ''.join([table.get(c, c) for c in arg])
+
+        return '"' + escaped + '"' if needs_quote(arg) else escaped
+
+    return " ".join([escape(arg) for arg in command])
+
+
+def decode(string):
+    """ Takes a command string and returns as a list. """
+
+    def unescape(arg):
+        """ Gets rid of the escaping characters. """
+
+        if len(arg) >= 2 and arg[0] == arg[-1] and arg[0] == '"':
+            arg = arg[1:-1]
+            return re.sub(r'\\(["\\])', r'\1', arg)
+        return re.sub(r'\\([\\ $%&\(\)\[\]\{\}\*|<>@?!])', r'\1', arg)
+
+    return [unescape(arg) for arg in shlex.split(string)]
Index: tools/scan-build-py/libscanbuild/runner.py
===================================================================
--- tools/scan-build-py/libscanbuild/runner.py
+++ tools/scan-build-py/libscanbuild/runner.py
@@ -9,11 +9,11 @@
 import logging
 import os
 import os.path
-import shlex
 import tempfile
 import functools
-from libscanbuild.command import classify_parameters, Action
+from libscanbuild.command import classify_parameters, Action, classify_source
 from libscanbuild.clang import get_arguments, get_version
+from libscanbuild.shell import decode
 
 __all__ = ['run']
 
@@ -29,7 +29,7 @@
     try:
         command = opts.pop('command')
         logging.debug("Run analyzer against '%s'", command)
-        opts.update(classify_parameters(shlex.split(command)))
+        opts.update(classify_parameters(decode(command)))
 
         return action_check(opts)
     except Exception:
@@ -87,7 +87,7 @@
                                       dir=destination(opts))
     os.close(handle)
     cwd = opts['directory']
-    cmd = get_arguments(cwd, [opts['clang']] + opts['report'] + ['-o', name])
+    cmd = get_arguments([opts['clang']] + opts['report'] + ['-o', name], cwd)
     logging.debug('exec command in %s: %s', cwd, ' '.join(cmd))
     subprocess.call(cmd, cwd=cwd)
 
@@ -116,7 +116,8 @@
     requested, it calls the continuation to generate it. """
 
     cwd = opts['directory']
-    cmd = [opts['clang']] + opts['analyze'] + opts['output']
+    cmd = get_arguments([opts['clang']] + opts['analyze'] + opts['output'],
+                        cwd)
     logging.debug('exec command in %s: %s', cwd, ' '.join(cmd))
     child = subprocess.Popen(cmd,
                              cwd=cwd,
@@ -170,7 +171,7 @@
         common.extend(['-arch', opts.pop('arch')])
     common.extend(opts.pop('compile_options', []))
     common.extend(['-x', opts['language']])
-    common.append(opts['file'])
+    common.append(os.path.relpath(opts['file'], opts['directory']))
 
     opts.update({
         'analyze': ['--analyze'] + opts['direct_args'] + common,
@@ -180,32 +181,11 @@
     return continuation(opts)
 
 
-@require(['file'])
+@require(['file', 'c++'])
 def language_check(opts, continuation=create_commands):
     """ Find out the language from command line parameters or file name
     extension. The decision also influenced by the compiler invocation. """
 
-    def from_filename(name, cplusplus_compiler):
-        """ Return the language from fille name extension. """
-
-        mapping = {
-            '.c': 'c++' if cplusplus_compiler else 'c',
-            '.cp': 'c++',
-            '.cpp': 'c++',
-            '.cxx': 'c++',
-            '.txx': 'c++',
-            '.cc': 'c++',
-            '.C': 'c++',
-            '.ii': 'c++-cpp-output',
-            '.i': 'c++-cpp-output' if cplusplus_compiler else 'c-cpp-output',
-            '.m': 'objective-c',
-            '.mi': 'objective-c-cpp-output',
-            '.mm': 'objective-c++',
-            '.mii': 'objective-c++-cpp-output'
-        }
-        (_, extension) = os.path.splitext(os.path.basename(name))
-        return mapping.get(extension)
-
     accepteds = {
         'c', 'c++', 'objective-c', 'objective-c++', 'c-cpp-output',
         'c++-cpp-output', 'objective-c-cpp-output'
@@ -213,7 +193,7 @@
 
     key = 'language'
     language = opts[key] if key in opts else \
-        from_filename(opts['file'], opts.get('cxx', False))
+        classify_source(opts['file'], opts['c++'])
 
     if language is None:
         logging.debug('skip analysis, language not known')
@@ -236,7 +216,7 @@
     key = 'archs_seen'
     if key in opts:
         # filter out disabled architectures and -arch switches
-        archs = [a for a in opts[key] if '-arch' != a and a not in disableds]
+        archs = [a for a in opts[key] if a not in disableds]
 
         if not archs:
             logging.debug('skip analysis, found not supported arch')
Index: tools/scan-build-py/libscanbuild/report.py
===================================================================
--- tools/scan-build-py/libscanbuild/report.py
+++ tools/scan-build-py/libscanbuild/report.py
@@ -420,17 +420,9 @@
 def copy_resource_files(output_dir):
     """ Copy the javascript and css files to the report directory. """
 
-    try:
-        import pkg_resources
-        package = 'libscanbuild'
-        resources_dir = pkg_resources.resource_filename(package, 'resources')
-        for resource in pkg_resources.resource_listdir(package, 'resources'):
-            shutil.copy(os.path.join(resources_dir, resource), output_dir)
-    except ImportError:
-        resources_dir = os.path.join(
-            os.path.dirname(os.path.realpath(__file__)), 'resources')
-        for resource in os.listdir(resources_dir):
-            shutil.copy(os.path.join(resources_dir, resource), output_dir)
+    this_dir = os.path.dirname(os.path.realpath(__file__))
+    for resource in os.listdir(os.path.join(this_dir, 'resources')):
+        shutil.copy(os.path.join(this_dir, 'resources', resource), output_dir)
 
 
 def encode_value(container, key, encode):
Index: tools/scan-build-py/libscanbuild/options.py
===================================================================
--- tools/scan-build-py/libscanbuild/options.py
+++ tools/scan-build-py/libscanbuild/options.py
@@ -61,6 +61,11 @@
         default=0,
         help="""Enable verbose output from '%(prog)s'. A second and third
                 '-v' increases verbosity.""")
+    parser.add_argument(
+        '--override-compiler',
+        action='store_true',
+        help="""Always resort to the compiler wrapper even when better
+                interposition methods are available.""")
     if add_cdb:
         parser.add_argument('--cdb',
                             metavar='<file>',
@@ -153,14 +158,12 @@
         '--maxloop', '-maxloop',
         metavar='<loop count>',
         type=int,
-        default=4,
         help="""Specifiy the number of times a block can be visited before
                 giving up. Increase for more comprehensive coverage at a cost
                 of speed.""")
     advanced.add_argument('--store', '-store',
                           metavar='<model>',
                           dest='store_model',
-                          default='region',
                           choices=['region', 'basic'],
                           help="""Specify the store model used by the analyzer.
                 'region' specifies a field- sensitive store model.
@@ -171,7 +174,6 @@
         '--constraints', '-constraints',
         metavar='<model>',
         dest='constraints_model',
-        default='range',
         choices=['range', 'basic'],
         help="""Specify the contraint engine used by the analyzer. Specifying
                 'basic' uses a simpler, less powerful constraint model used by
@@ -241,11 +243,11 @@
         help="""Loading external checkers using the clang plugin interface.""")
     plugins.add_argument('--enable-checker', '-enable-checker',
                          metavar='<checker name>',
-                         action='append',
+                         action=AppendCommaSeparated,
                          help="""Enable specific checker.""")
     plugins.add_argument('--disable-checker', '-disable-checker',
                          metavar='<checker name>',
-                         action='append',
+                         action=AppendCommaSeparated,
                          help="""Disable specific checker.""")
     plugins.add_argument(
         '--help-checkers',
@@ -258,3 +260,16 @@
         '--help-checkers-verbose',
         action='store_true',
         help="""Print all available checkers and mark the enabled ones.""")
+
+
+class AppendCommaSeparated(argparse.Action):
+    """ argparse Action class to support multiple comma separated lists. """
+
+    def __call__(self, __parser, namespace, values, __option_string):
+        # getattr(obj, attr, default) does not really returns default but none
+        if getattr(namespace, self.dest, None) is None:
+            setattr(namespace, self.dest, [])
+        # once it's fixed we can use as expected
+        actual = getattr(namespace, self.dest)
+        actual.extend(values.split(','))
+        setattr(namespace, self.dest, actual)
Index: tools/scan-build-py/libscanbuild/interposition.py
===================================================================
--- tools/scan-build-py/libscanbuild/interposition.py
+++ tools/scan-build-py/libscanbuild/interposition.py
@@ -13,12 +13,11 @@
                                   build_command)
 from libscanbuild.driver import (initialize_logging, ReportDirectory,
                                  analyzer_params, print_checkers,
-                                 print_active_checkers)
+                                 print_active_checkers, need_analyzer)
 from libscanbuild.report import document
 from libscanbuild.clang import get_checkers
 from libscanbuild.runner import action_check
-from libscanbuild.intercept import is_source_file
-from libscanbuild.command import classify_parameters
+from libscanbuild.command import classify_parameters, classify_source
 
 __all__ = ['main', 'wrapper']
 
@@ -82,7 +81,7 @@
         'CXX': os.path.join(wrapper_dir, 'analyze-cxx'),
         'BUILD_ANALYZE_CC': args.cc,
         'BUILD_ANALYZE_CXX': args.cxx,
-        'BUILD_ANALYZE_CLANG': args.clang,
+        'BUILD_ANALYZE_CLANG': args.clang if need_analyzer(args.build) else '',
         'BUILD_ANALYZE_VERBOSE': 'DEBUG' if args.verbose > 2 else 'WARNING',
         'BUILD_ANALYZE_REPORT_DIR': destination,
         'BUILD_ANALYZE_REPORT_FORMAT': args.output_format,
@@ -104,6 +103,10 @@
     compilation = [compiler] + sys.argv[1:]
     logging.info('execute compiler: %s', compilation)
     result = subprocess.call(compilation)
+    # exit when it fails, ...
+    if result or not os.getenv('BUILD_ANALYZE_CLANG'):
+        return result
+    # ... and run the analyzer if all went well.
     try:
         # collect the needed parameters from environment, crash when missing
         consts = {
@@ -118,7 +121,7 @@
         # get relevant parameters from command line arguments
         args = classify_parameters(sys.argv)
         filenames = args.pop('files', [])
-        for filename in (name for name in filenames if is_source_file(name)):
+        for filename in (name for name in filenames if classify_source(name)):
             parameters = dict(args, file=filename, **consts)
             logging.debug('analyzer parameters %s', parameters)
             current = action_check(parameters)
@@ -128,5 +131,5 @@
                     logging.info(line.rstrip())
     except Exception:
         logging.exception("run analyzer inside compiler wrapper failed.")
-    # return compiler exit code
-    return result
+    finally:
+        return 0
Index: tools/scan-build-py/libscanbuild/intercept.py
===================================================================
--- tools/scan-build-py/libscanbuild/intercept.py
+++ tools/scan-build-py/libscanbuild/intercept.py
@@ -26,10 +26,12 @@
 import os
 import os.path
 import re
-import shlex
+import glob
 import itertools
+from libear import ear_library
 from libscanbuild import duplicate_check, tempdir
 from libscanbuild.command import Action, classify_parameters
+from libscanbuild.shell import encode, decode
 
 __all__ = ['capture', 'wrapper']
 
@@ -48,7 +50,7 @@
             current = itertools.chain.from_iterable(
                 # creates a sequence of entry generators from an exec,
                 # but filter out non compiler calls before.
-                (format_entry(x) for x in commands if is_compiler_call(x)))
+                (format_entry(x) for x in commands if compiler_call(x)))
             # read entries from previous run
             if 'append' in args and args.append and os.path.exists(args.cdb):
                 with open(args.cdb) as handle:
@@ -57,7 +59,8 @@
                 previous = iter([])
             # filter out duplicate entries from both
             duplicate = duplicate_check(entry_hash)
-            return (entry for entry in itertools.chain(previous, current)
+            return (entry
+                    for entry in itertools.chain(previous, current)
                     if os.path.exists(entry['file']) and not duplicate(entry))
         return commands
 
@@ -66,10 +69,11 @@
         environment = setup_environment(args, tmpdir, wrappers_dir)
         logging.debug('run build in environment: %s', environment)
         exit_code = subprocess.call(args.build, env=environment)
-        logging.debug('build finished with exit code: %d', exit_code)
+        logging.info('build finished with exit code: %d', exit_code)
         # read the intercepted exec calls
-        commands = (parse_exec_trace(os.path.join(tmpdir, filename))
-                    for filename in sorted(os.listdir(tmpdir)))
+        commands = (
+            parse_exec_trace(os.path.join(tmpdir, filename))
+            for filename in sorted(glob.iglob(os.path.join(tmpdir, '*.cmd'))))
         # do post processing
         entries = post_processing(itertools.chain.from_iterable(commands))
         # dump the compilation database
@@ -85,10 +89,13 @@
     The exec calls will be logged by the 'libear' preloaded library or by the
     'wrapper' programs. """
 
+    compiler = args.cc if 'cc' in args.__dict__ else 'cc'
+    ear_library_path = ear_library(compiler, destination)
+
     environment = dict(os.environ)
     environment.update({'BUILD_INTERCEPT_TARGET_DIR': destination})
 
-    if sys.platform in {'win32', 'cygwin'} or not ear_library_path(False):
+    if args.override_compiler or not ear_library_path:
         environment.update({
             'CC': os.path.join(wrappers_dir, 'intercept-cc'),
             'CXX': os.path.join(wrappers_dir, 'intercept-cxx'),
@@ -98,11 +105,11 @@
         })
     elif 'darwin' == sys.platform:
         environment.update({
-            'DYLD_INSERT_LIBRARIES': ear_library_path(True),
+            'DYLD_INSERT_LIBRARIES': ear_library_path,
             'DYLD_FORCE_FLAT_NAMESPACE': '1'
         })
     else:
-        environment.update({'LD_PRELOAD': ear_library_path(False)})
+        environment.update({'LD_PRELOAD': ear_library_path})
 
     return environment
 
@@ -152,6 +159,7 @@
     generated by the interception library or wrapper command. A single
     report file _might_ contain multiple process creation info. """
 
+    logging.debug('parse exec trace file: %s', filename)
     with open(filename, 'r') as handler:
         content = handler.read()
         for group in filter(bool, content.split(GS)):
@@ -168,54 +176,34 @@
 def format_entry(entry):
     """ Generate the desired fields for compilation database entries. """
 
-    def join_command(args):
-        return ' '.join([shell_escape(arg) for arg in args])
-
     def abspath(cwd, name):
         """ Create normalized absolute path from input filename. """
         fullname = name if os.path.isabs(name) else os.path.join(cwd, name)
         return os.path.normpath(fullname)
 
+    logging.debug('format this command: %s', entry['command'])
     atoms = classify_parameters(entry['command'])
-    return ({
-        'directory': entry['directory'],
-        'command': join_command(entry['command']),
-        'file': abspath(entry['directory'], filename)
-    } for filename in atoms.get('files', [])
-            if is_source_file(filename) and atoms['action'] <= Action.Compile)
-
-
-def shell_escape(arg):
-    """ Create a single string from list.
-
-    The major challenge, to deal with white spaces. Which are used by
-    the shell as separator. (Eg.: -D_KEY="Value with spaces") """
-
-    def quote(arg):
-        table = {'\\': '\\\\', '"': '\\"', "'": "\\'"}
-        return '"' + ''.join([table.get(c, c) for c in arg]) + '"'
-
-    return quote(arg) if len(shlex.split(arg)) > 1 else arg
-
-
-def is_source_file(filename):
-    """ A predicate to decide the filename is a source file or not. """
-
-    accepted = {
-        '.c', '.C', '.cc', '.CC', '.cxx', '.cp', '.cpp', '.c++', '.m', '.mm',
-        '.i', '.ii', '.mii'
-    }
-    _, ext = os.path.splitext(filename)
-    return ext in accepted
+    if atoms['action'] <= Action.Compile:
+        for source in atoms['files']:
+            compiler = 'c++' if atoms['c++'] else 'cc'
+            output = ['-o', atoms['output']] if atoms.get('output') else []
+            command = [compiler, '-c'
+                       ] + atoms['compile_options'] + output + [source]
+            logging.debug('formated as: %s', command)
+            yield {
+                'directory': entry['directory'],
+                'command': encode(command),
+                'file': abspath(entry['directory'], source)
+            }
 
 
-def is_compiler_call(entry):
+def compiler_call(entry):
     """ A predicate to decide the entry is a compiler call or not. """
 
     patterns = [
         re.compile(r'^([^/]*/)*intercept-c(c|\+\+)$'),
         re.compile(r'^([^/]*/)*c(c|\+\+)$'),
-        re.compile(r'^([^/]*/)*([^-]*-)*g(cc|\+\+)(-\d+(\.\d+){0,2})?$'),
+        re.compile(r'^([^/]*/)*([^-]*-)*[mg](cc|\+\+)(-\d+(\.\d+){0,2})?$'),
         re.compile(r'^([^/]*/)*([^-]*-)*clang(\+\+)?(-\d+(\.\d+){0,2})?$'),
         re.compile(r'^([^/]*/)*llvm-g(cc|\+\+)$'),
     ]
@@ -234,23 +222,11 @@
     # 'clang' therefore both call would be logged. To avoid
     # this the hash does not contain the first word of the
     # command.
-    command = ' '.join(shlex.split(entry['command'])[1:])
+    command = ' '.join(decode(entry['command'])[1:])
 
     return '<>'.join([filename, directory, command])
 
 
-def ear_library_path(darwin):
-    """ Returns the full path to the 'libear' library. """
-
-    try:
-        import pkg_resources
-        lib_name = 'libear.dylib' if darwin else 'libear.so'
-        lib_file = pkg_resources.resource_filename('libscanbuild', lib_name)
-        return lib_file if os.path.exists(lib_file) else None
-    except ImportError:
-        return None
-
-
 if sys.version_info.major >= 3 and sys.version_info.minor >= 2:
     from tempfile import TemporaryDirectory
 else:
Index: tools/scan-build-py/libscanbuild/driver.py
===================================================================
--- tools/scan-build-py/libscanbuild/driver.py
+++ tools/scan-build-py/libscanbuild/driver.py
@@ -20,7 +20,6 @@
 import json
 import tempfile
 import multiprocessing
-from libscanbuild import tempdir
 from libscanbuild.runner import run
 from libscanbuild.intercept import capture
 from libscanbuild.options import create_parser
@@ -50,14 +49,17 @@
 
         # next step to run the analyzer against the captured commands
         with ReportDirectory(args.output, args.keep_empty) as target_dir:
-            run_analyzer(args, target_dir.name)
-            # cover report generation and bug counting
-            number_of_bugs = document(args, target_dir.name, True)
-            # remove the compilation database when it was not requested
-            if args.action == 'all' and os.path.exists(args.cdb):
-                os.unlink(args.cdb)
-            # set exit status as it was requested
-            return number_of_bugs if args.status_bugs else exit_code
+            if args.action == 'analyze' or need_analyzer(args.build):
+                run_analyzer(args, target_dir.name)
+                # cover report generation and bug counting
+                number_of_bugs = document(args, target_dir.name, True)
+                # remove the compilation database when it was not requested
+                if args.action == 'all' and os.path.exists(args.cdb):
+                    os.unlink(args.cdb)
+                # set exit status as it was requested
+                return number_of_bugs if args.status_bugs else exit_code
+            else:
+                return exit_code
     except KeyboardInterrupt:
         return 1
     except Exception:
@@ -103,6 +105,12 @@
         parser.error('missing build command')
 
 
+def need_analyzer(args):
+    """ Check the internt of the build command. """
+
+    return len(args) and not re.search('configure|autogen', args[0])
+
+
 def run_analyzer(args, output_dir):
     """ Runs the analyzer against the given compilation database. """
 
@@ -164,15 +172,16 @@
         result.append('-analyzer-output={0}'.format(args.output_format))
     if args.analyzer_config:
         result.append(args.analyzer_config)
-    if 2 <= args.verbose:
+    if 4 <= args.verbose:
         result.append('-analyzer-display-progress')
     if args.plugins:
         result.extend(prefix_with('-load', args.plugins))
     if args.enable_checker:
-        result.extend(prefix_with('-analyzer-checker', args.enable_checker))
+        checkers = ','.join(args.enable_checker)
+        result.extend(['-analyzer-checker', checkers])
     if args.disable_checker:
-        result.extend(
-            prefix_with('-analyzer-disable-checker', args.disable_checker))
+        checkers = ','.join(args.disable_checker)
+        result.extend(['-analyzer-disable-checker', checkers])
     if os.getenv('UBIVIZ'):
         result.append('-analyzer-viz-egraph-ubigraph')
 
@@ -215,6 +224,7 @@
     def __init__(self, hint, keep):
         self.name = ReportDirectory._create(hint)
         self.keep = keep
+        logging.info('Report directory created: %s', self.name)
 
     def __enter__(self):
         return self
@@ -235,12 +245,5 @@
 
     @staticmethod
     def _create(hint):
-        if tempdir() != hint:
-            try:
-                os.mkdir(hint)
-                return hint
-            except OSError:
-                raise
-        else:
-            stamp = time.strftime('%Y-%m-%d-%H%M%S', time.localtime())
-            return tempfile.mkdtemp(prefix='scan-build-{0}-'.format(stamp))
+        stamp = time.strftime('scan-build-%Y-%m-%d-%H%M%S-', time.localtime())
+        return tempfile.mkdtemp(prefix=stamp, dir=hint)
Index: tools/scan-build-py/libscanbuild/command.py
===================================================================
--- tools/scan-build-py/libscanbuild/command.py
+++ tools/scan-build-py/libscanbuild/command.py
@@ -6,264 +6,121 @@
 """ This module is responsible for to parse a compiler invocation. """
 
 import re
+import os
 
-__all__ = ['Action', 'classify_parameters']
+__all__ = ['Action', 'classify_parameters', 'classify_source']
 
 
 class Action(object):
     """ Enumeration class for compiler action. """
 
-    Link, Compile, Preprocess, Info, Internal = range(5)
+    Link, Compile, Ignored = range(3)
 
 
 def classify_parameters(command):
-    """ Parses the command line arguments of the given invocation.
-
-    To run analysis from a compilation command, first it disassembles the
-    compilation command. Classifies the parameters into groups and throws
-    away those which are not relevant. """
-
-    def match(state, iterator):
-        """ This method contains a list of pattern and action tuples.
-        The matching start from the top if the list, when the first match
-        happens the action is executed. """
-
-        def regex(pattern, action):
-            """ Matching expression for regex. """
-
-            def evaluate(iterator):
-                match = evaluate.regexp.match(iterator.current())
-                if match:
-                    action(state, iterator, match)
-                    return True
-
-            evaluate.regexp = re.compile(pattern)
-            return evaluate
-
-        def anyof(opts, action):
-            """ Matching expression for string literals. """
-
-            def evaluate(iterator):
-                if iterator.current() in opts:
-                    action(state, iterator, None)
-                    return True
-
-            return evaluate
-
-        tasks = [
-            # actions
-            regex(r'^-(E|MM?)$', take_action(Action.Preprocess)),
-            anyof({'-c'}, take_action(Action.Compile)),
-            anyof({'-print-prog-name'}, take_action(Action.Info)),
-            anyof({'-cc1'}, take_action(Action.Internal)),
-            # architectures
-            anyof({'-arch'}, take_two('archs_seen')),
-            # module names
-            anyof({'-filelist'}, take_from_file('files')),
-            regex(r'^[^-].+', take_one('files')),
-            # language
-            anyof({'-x'}, take_second('language')),
-            # output
-            anyof({'-o'}, take_second('output')),
-            # relevant compiler flags
-            anyof({'-write-strings', '-v'}, take_one('compile_options')),
-            anyof({'-ftrapv-handler', '--sysroot', '-target'},
-                  take_two('compile_options')),
-            regex(r'^-isysroot', take_two('compile_options')),
-            regex(r'^-m(32|64)$', take_one('compile_options')),
-            regex(r'^-mios-simulator-version-min(.*)',
-                  take_joined('compile_options')),
-            regex(r'^-stdlib(.*)', take_joined('compile_options')),
-            regex(r'^-mmacosx-version-min(.*)',
-                  take_joined('compile_options')),
-            regex(r'^-miphoneos-version-min(.*)',
-                  take_joined('compile_options')),
-            regex(r'^-O[1-3]$', take_one('compile_options')),
-            anyof({'-O'}, take_as('-O1', 'compile_options')),
-            anyof({'-Os'}, take_as('-O2', 'compile_options')),
-            regex(r'^-[DIU](.*)$', take_joined('compile_options')),
-            regex(r'^-isystem(.*)$', take_joined('compile_options')),
-            anyof({'-nostdinc'}, take_one('compile_options')),
-            regex(r'^-std=', take_one('compile_options')),
-            regex(r'^-include', take_two('compile_options')),
-            anyof({
-                '-idirafter', '-imacros', '-iprefix', '-iwithprefix',
-                '-iwithprefixbefore'
-            }, take_two('compile_options')),
-            regex(r'^-m.*', take_one('compile_options')),
-            regex(r'^-iquote(.*)', take_joined('compile_options')),
-            regex(r'^-Wno-', take_one('compile_options')),
-            # ignored flags
-            regex(r'^-framework$', take_two()),
-            regex(r'^-fobjc-link-runtime(.*)', take_joined()),
-            regex(r'^-[lL]', take_one()),
-            regex(r'^-M[TF]$', take_two()),
-            regex(r'^-[eu]$', take_two()),
-            anyof({'-fsyntax-only', '-save-temps'}, take_one()),
-            anyof({
-                '-install_name', '-exported_symbols_list', '-current_version',
-                '-compatibility_version', '-init', '-seg1addr',
-                '-bundle_loader', '-multiply_defined', '--param',
-                '--serialize-diagnostics'
-            }, take_two()),
-            anyof({'-sectorder'}, take_four()),
-            # relevant compiler flags
-            regex(r'^-[fF](.+)$', take_one('compile_options'))
-        ]
-        for task in tasks:
-            if task(iterator):
-                return
-
-    state = {'action': Action.Link, 'cxx': is_cplusplus_compiler(command[0])}
-
-    arguments = Arguments(command)
-    for _ in arguments:
-        match(state, arguments)
-    return state
-
-
-class Arguments(object):
-    """ An iterator wraper around compiler arguments.
-
-    Python iterators are only implement the 'next' method, but this one
-    implements the 'current' query method as well. """
-
-    def __init__(self, args):
-        """ Takes full command line, but iterates on the parameters only. """
-
-        self.__sequence = [arg for arg in args[1:] if arg != '']
-        self.__size = len(self.__sequence)
-        self.__current = -1
-
-    def __iter__(self):
-        """ Needed for python iterator. """
-
-        return self
-
-    def __next__(self):
-        """ Needed for python iterator. (version 3.x) """
-
-        return self.next()
-
-    def next(self):
-        """ Needed for python iterator. (version 2.x) """
-
-        self.__current += 1
-        return self.current()
-
-    def current(self):
-        """ Extra method to query the current element. """
-
-        if self.__current >= self.__size:
-            raise StopIteration
+    """ Parses the command line arguments of the given invocation. """
+
+    ignored = {
+        '-g': 0,
+        '-fsyntax-only': 0,
+        '-save-temps': 0,
+        '-install_name': 1,
+        '-exported_symbols_list': 1,
+        '-current_version': 1,
+        '-compatibility_version': 1,
+        '-init': 1,
+        '-e': 1,
+        '-seg1addr': 1,
+        '-bundle_loader': 1,
+        '-multiply_defined': 1,
+        '-sectorder': 3,
+        '--param': 1,
+        '--serialize-diagnostics': 1
+    }
+
+    state = {
+        'action': Action.Link,
+        'files': [],
+        'output': None,
+        'compile_options': [],
+        'c++': cplusplus_compiler(command[0])
+    }
+
+    args = iter(command[1:])
+    for arg in args:
+        # compiler action parameters are the most important ones...
+        if arg in {'-E', '-S', '-cc1', '-M', '-MM', '-###'}:
+            state.update({'action': Action.Ignored})
+        elif arg == '-c':
+            state.update({'action': max(state['action'], Action.Compile)})
+        # arch flags are taken...
+        elif arg == '-arch':
+            archs = state.get('archs_seen', [])
+            state.update({'archs_seen': archs + [next(args)]})
+        # explicit language option taken...
+        elif arg == '-x':
+            state.update({'language': next(args)})
+        # output flag taken...
+        elif arg == '-o':
+            state.update({'output': next(args)})
+        # warning disable options are taken...
+        elif re.match(r'^-Wno-', arg):
+            state['compile_options'].append(arg)
+        # warning options are ignored...
+        elif re.match(r'^-[mW].+', arg):
+            pass
+        # some preprocessor parameters are ignored...
+        elif arg in {'-MD', '-MMD', '-MG', '-MP'}:
+            pass
+        elif arg in {'-MF', '-MT', '-MQ'}:
+            next(args)
+        # linker options are ignored...
+        elif arg in {'-static', '-shared', '-s', '-rdynamic'} or \
+                re.match(r'^-[lL].+', arg):
+            pass
+        elif arg in {'-l', '-L', '-u', '-z', '-T', '-Xlinker'}:
+            next(args)
+        # some other options are ignored...
+        elif arg in ignored.keys():
+            for _ in range(ignored[arg]):
+                next(args)
+        # parameters which looks source file are taken...
+        elif re.match(r'^[^-].+', arg) and classify_source(arg):
+            state['files'].append(arg)
+        # and consider everything else as compile option.
         else:
-            return self.__sequence[self.__current]
-
-
-def take_n(count=1, *keys):
-    """ Take N number of arguments and append it to the refered values. """
-
-    def take(values, iterator, _match):
-        updates = []
-        updates.append(iterator.current())
-        for _ in range(count - 1):
-            updates.append(iterator.next())
-        for key in keys:
-            current = values.get(key, [])
-            values.update({key: current + updates})
-
-    return take
-
-
-def take_one(*keys):
-    """ Take one argument and append to the 'key' values. """
+            state['compile_options'].append(arg)
 
-    return take_n(1, *keys)
-
-
-def take_two(*keys):
-    """ Take two arguments and append to the 'key' values. """
-
-    return take_n(2, *keys)
-
-
-def take_four(*keys):
-    """ Take four arguments and append to the 'key' values. """
-
-    return take_n(4, *keys)
-
-
-def take_joined(*keys):
-    """ Take one or two arguments and append to the 'key' values.
-
-    eg.: '-Isomething' shall take only one.
-         '-I something' shall take two.
-
-    This action should go with regex matcher only. """
-
-    def take(values, iterator, match):
-        updates = []
-        updates.append(iterator.current())
-        if not match.group(1):
-            updates.append(iterator.next())
-        for key in keys:
-            current = values.get(key, [])
-            values.update({key: current + updates})
-
-    return take
-
-
-def take_from_file(*keys):
-    """ Take values from the refered file and append to the 'key' values.
-
-    The refered file is the second argument. (So it consume two args.) """
-
-    def take(values, iterator, _match):
-        with open(iterator.next()) as handle:
-            current = [line.strip() for line in handle.readlines()]
-            for key in keys:
-                values[key] = current
-
-    return take
-
-
-def take_as(value, *keys):
-    """ Take one argument and append to the 'key' values.
-
-    But instead of taking the argument, it takes the value as it was given. """
-
-    def take(values, _iterator, _match):
-        updates = [value]
-        for key in keys:
-            current = values.get(key, [])
-            values.update({key: current + updates})
-
-    return take
-
-
-def take_second(*keys):
-    """ Take the second argument and append to the 'key' values. """
-
-    def take(values, iterator, _match):
-        current = iterator.next()
-        for key in keys:
-            values[key] = current
-
-    return take
-
-
-def take_action(action):
-    """ Take the action value and overwrite current value if that's bigger. """
-
-    def take(values, _iterator, _match):
-        key = 'action'
-        current = values[key]
-        values[key] = max(current, action)
-
-    return take
+    return state
 
 
-def is_cplusplus_compiler(name):
+def classify_source(filename, cplusplus=False):
+    """ Return the language from fille name extension. """
+
+    mapping = {
+        '.c': 'c++' if cplusplus else 'c',
+        '.i': 'c++-cpp-output' if cplusplus else 'c-cpp-output',
+        '.ii': 'c++-cpp-output',
+        '.m': 'objective-c',
+        '.mi': 'objective-c-cpp-output',
+        '.mm': 'objective-c++',
+        '.mii': 'objective-c++-cpp-output',
+        '.C': 'c++',
+        '.cc': 'c++',
+        '.CC': 'c++',
+        '.cp': 'c++',
+        '.cpp': 'c++',
+        '.cxx': 'c++',
+        '.c++': 'c++',
+        '.C++': 'c++',
+        '.txx': 'c++'
+    }
+
+    __, extension = os.path.splitext(os.path.basename(filename))
+    return mapping.get(extension)
+
+
+def cplusplus_compiler(name):
     """ Returns true when the compiler name refer to a C++ compiler. """
 
     match = re.match(r'^([^/]*/)*(\w*-)*(\w+\+\+)(-(\d+(\.\d+){0,3}))?$', name)
Index: tools/scan-build-py/libscanbuild/clang.py
===================================================================
--- tools/scan-build-py/libscanbuild/clang.py
+++ tools/scan-build-py/libscanbuild/clang.py
@@ -11,7 +11,7 @@
 import subprocess
 import logging
 import re
-import shlex
+from libscanbuild.shell import decode
 
 __all__ = ['get_version', 'get_arguments', 'get_checkers']
 
@@ -23,7 +23,7 @@
     return lines.decode('ascii').splitlines()[0]
 
 
-def get_arguments(cwd, command):
+def get_arguments(command, cwd):
     """ Capture Clang invocation.
 
     Clang can be executed directly (when you just ask specific action to
@@ -41,10 +41,6 @@
             raise Exception("output not found")
         return last
 
-    def strip_quotes(quoted):
-        match = re.match(r'^\"([^\"]*)\"$', quoted)
-        return match.group(1) if match else quoted
-
     cmd = command[:]
     cmd.insert(1, '-###')
     logging.debug('exec command in %s: %s', cwd, ' '.join(cmd))
@@ -57,9 +53,9 @@
     child.stdout.close()
     child.wait()
     if 0 == child.returncode:
-        if re.match(r'^clang: error:', line):
+        if re.search(r'clang(.*): error:', line):
             raise Exception(line)
-        return [strip_quotes(x) for x in shlex.split(line)]
+        return decode(line)
     else:
         raise Exception(line)
 
@@ -77,7 +73,7 @@
                                                         '-Xclang', plugin]]
         cmd = [clang, '--analyze'] + load + ['-x', language, '-']
         pattern = re.compile(r'^-analyzer-checker=(.*)$')
-        return [pattern.match(arg).group(1) for arg in get_arguments('.', cmd)
+        return [pattern.match(arg).group(1) for arg in get_arguments(cmd, '.')
                 if pattern.match(arg)]
 
     result = set()
Index: tools/scan-build-py/libear/ear.c
===================================================================
--- tools/scan-build-py/libear/ear.c
+++ tools/scan-build-py/libear/ear.c
@@ -28,6 +28,7 @@
 #include <string.h>
 #include <unistd.h>
 #include <dlfcn.h>
+#include <pthread.h>
 
 #if defined HAVE_POSIX_SPAWN || defined HAVE_POSIX_SPAWNP
 #include <spawn.h>
@@ -40,6 +41,27 @@
 extern char **environ;
 #endif
 
+#define ENV_OUTPUT "BUILD_INTERCEPT_TARGET_DIR"
+#ifdef APPLE
+# define ENV_FLAT    "DYLD_FORCE_FLAT_NAMESPACE"
+# define ENV_PRELOAD "DYLD_INSERT_LIBRARIES"
+# define ENV_SIZE 3
+#else
+# define ENV_PRELOAD "LD_PRELOAD"
+# define ENV_SIZE 2
+#endif
+
+#define DLSYM(TYPE_, VAR_, SYMBOL_)                                            \
+    union {                                                                    \
+        void *from;                                                            \
+        TYPE_ to;                                                              \
+    } cast;                                                                    \
+    if (0 == (cast.from = dlsym(RTLD_NEXT, SYMBOL_))) {                        \
+        perror("bear: dlsym");                                                 \
+        exit(EXIT_FAILURE);                                                    \
+    }                                                                          \
+    TYPE_ const VAR_ = cast.to;
+
 
 typedef char const * bear_env_t[ENV_SIZE];
 
@@ -72,23 +94,12 @@
     };
 
 static int initialized = 0;
+static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
 
 static void on_load(void) __attribute__((constructor));
 static void on_unload(void) __attribute__((destructor));
 
 
-#define DLSYM(TYPE_, VAR_, SYMBOL_)                                            \
-    union {                                                                    \
-        void *from;                                                            \
-        TYPE_ to;                                                              \
-    } cast;                                                                    \
-    if (0 == (cast.from = dlsym(RTLD_NEXT, SYMBOL_))) {                        \
-        perror("bear: dlsym");                                                 \
-        exit(EXIT_FAILURE);                                                    \
-    }                                                                          \
-    TYPE_ const VAR_ = cast.to;
-
-
 #ifdef HAVE_EXECVE
 static int call_execve(const char *path, char *const argv[],
                        char *const envp[]);
@@ -124,16 +135,20 @@
  */
 
 static void on_load(void) {
+    pthread_mutex_lock(&mutex);
 #ifdef HAVE_NSGETENVIRON
     environ = *_NSGetEnviron();
 #endif
     if (!initialized)
         initialized = bear_capture_env_t(&initial_env);
+    pthread_mutex_unlock(&mutex);
 }
 
 static void on_unload(void) {
+    pthread_mutex_lock(&mutex);
     bear_release_env_t(&initial_env);
     initialized = 0;
+    pthread_mutex_unlock(&mutex);
 }
 
 
@@ -373,6 +388,7 @@
     if (!initialized)
         return;
 
+    pthread_mutex_lock(&mutex);
     const char *cwd = getcwd(NULL, 0);
     if (0 == cwd) {
         perror("bear: getcwd");
@@ -404,6 +420,7 @@
         exit(EXIT_FAILURE);
     }
     free((void *)cwd);
+    pthread_mutex_unlock(&mutex);
 }
 
 /* update environment assure that chilren processes will copy the desired
Index: tools/scan-build-py/libear/config.h.in
===================================================================
--- tools/scan-build-py/libear/config.h.in
+++ tools/scan-build-py/libear/config.h.in
@@ -20,14 +20,3 @@
 #cmakedefine HAVE_NSGETENVIRON
 
 #cmakedefine APPLE
-
-#define ENV_OUTPUT "BUILD_INTERCEPT_TARGET_DIR"
-
-#ifdef APPLE
-# define ENV_FLAT    "DYLD_FORCE_FLAT_NAMESPACE"
-# define ENV_PRELOAD "DYLD_INSERT_LIBRARIES"
-# define ENV_SIZE 3
-#else
-# define ENV_PRELOAD "LD_PRELOAD"
-# define ENV_SIZE 2
-#endif
Index: tools/scan-build-py/libear/__init__.py
===================================================================
--- /dev/null
+++ tools/scan-build-py/libear/__init__.py
@@ -0,0 +1,284 @@
+# -*- coding: utf-8 -*-
+#                     The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+"""
+. """
+
+import sys
+import os
+import os.path
+import re
+import logging
+
+__all__ = ['ear_library']
+
+
+def ear_library(compiler, dst_dir):
+    """ Returns the full path to the 'libear' library. """
+
+    try:
+        src_dir = os.path.dirname(os.path.realpath(__file__))
+        with make_context(src_dir) as context:
+            context.set_compiler(compiler)
+            context.set_language_standard('c99')
+            context.add_definitions(['-D_GNU_SOURCE'])
+
+            with Configure(context) as configure:
+                configure.check_function_exists('execve', 'HAVE_EXECVE')
+                configure.check_function_exists('execv', 'HAVE_EXECV')
+                configure.check_function_exists('execvpe', 'HAVE_EXECVPE')
+                configure.check_function_exists('execvp', 'HAVE_EXECVP')
+                configure.check_function_exists('execvP', 'HAVE_EXECVP2')
+                configure.check_function_exists('execl', 'HAVE_EXECL')
+                configure.check_function_exists('execlp', 'HAVE_EXECLP')
+                configure.check_function_exists('execle', 'HAVE_EXECLE')
+                configure.check_function_exists('posix_spawn',
+                                                'HAVE_POSIX_SPAWN')
+                configure.check_function_exists('posix_spawnp',
+                                                'HAVE_POSIX_SPAWNP')
+                configure.check_symbol_exists('_NSGetEnviron', 'crt_externs.h',
+                                              'HAVE_NSGETENVIRON')
+                configure.write_by_template(
+                    os.path.join(src_dir, 'config.h.in'),
+                    os.path.join(dst_dir, 'config.h'))
+            with SharedLibrary('ear', context) as target:
+                target.add_include(dst_dir)
+                target.add_sources('ear.c')
+                target.link_against(context.dl_libraries())
+                target.link_against(['pthread'])
+                target.build_release(dst_dir)
+                return os.path.join(dst_dir, target.name)
+
+    except Exception:
+        logging.info("Could not build interception library.", exc_info=True)
+        return None
+
+
+def execute(cmd, *args, **kwargs):
+    """ Make subprocess execution silent. """
+
+    import subprocess
+    kwargs.update({'stdout': subprocess.PIPE, 'stderr': subprocess.STDOUT})
+    return subprocess.check_call(cmd, *args, **kwargs)
+
+
+class TemporaryDirectory(object):
+    """ This function creates a temporary directory using mkdtemp() (the
+    supplied arguments are passed directly to the underlying function).
+    The resulting object can be used as a context manager. On completion
+    of the context or destruction of the temporary directory object the
+    newly created temporary directory and all its contents are removed
+    from the filesystem. """
+
+    def __init__(self, **kwargs):
+        from tempfile import mkdtemp
+        self.name = mkdtemp(**kwargs)
+
+    def __enter__(self):
+        return self.name
+
+    def __exit__(self, _type, _value, _traceback):
+        self.cleanup()
+
+    def cleanup(self):
+        from shutil import rmtree
+        if self.name is not None:
+            rmtree(self.name)
+
+
+class Context:
+    """ Abstract class to represent different toolset. """
+
+    def __init__(self, src_dir):
+        self.src_dir = src_dir
+        self.compiler = None
+        self.c_flags = []
+
+    def __enter__(self):
+        """ declared to work 'with'. """
+        return self
+
+    def __exit__(self, _type, _value, _traceback):
+        """ declared to work 'with'. """
+        pass
+
+    def set_compiler(self, compiler):
+        """ part of public interface """
+        self.compiler = compiler
+
+    def set_language_standard(self, standard):
+        """ part of public interface """
+        self.c_flags.append('-std=' + standard)
+
+    def add_definitions(self, defines):
+        """ part of public interface """
+        self.c_flags.extend(defines)
+
+    def dl_libraries(self):
+        pass
+
+    def shared_library_name(self, name):
+        pass
+
+    def shared_library_c_flags(self, release):
+        extra = ['-DNDEBUG', '-O3'] if release else []
+        return extra + ['-fPIC'] + self.c_flags
+
+    def shared_library_ld_flags(self, release, name):
+        pass
+
+
+class DarwinContext(Context):
+    def __init__(self, src_dir):
+        Context.__init__(self, src_dir)
+
+    def dl_libraries(self):
+        return []
+
+    def shared_library_name(self, name):
+        return 'lib' + name + '.dylib'
+
+    def shared_library_ld_flags(self, release, name):
+        extra = ['-dead_strip'] if release else []
+        return extra + ['-dynamiclib', '-install_name', '@rpath/' + name]
+
+
+class UnixContext(Context):
+    def __init__(self, src_dir):
+        Context.__init__(self, src_dir)
+
+    def dl_libraries(self):
+        return []
+
+    def shared_library_name(self, name):
+        return 'lib' + name + '.so'
+
+    def shared_library_ld_flags(self, release, name):
+        extra = [] if release else []
+        return extra + ['-shared', '-Wl,-soname,' + name]
+
+
+class LinuxContext(UnixContext):
+    def __init__(self, src_dir):
+        UnixContext.__init__(self, src_dir)
+
+    def dl_libraries(self):
+        return ['dl']
+
+
+def make_context(src_dir):
+    platform = sys.platform
+    if platform in {'win32', 'cygwin'}:
+        raise RuntimeError('not implemented on this platform')
+    elif platform == 'darwin':
+        return DarwinContext(src_dir)
+    elif platform in {'linux', 'linux2'}:
+        return LinuxContext(src_dir)
+    else:
+        return UnixContext(src_dir)
+
+
+class Configure:
+    def __init__(self, context):
+        self.ctx = context
+        self.results = {'APPLE': sys.platform == 'darwin'}
+
+    def __enter__(self):
+        return self
+
+    def __exit__(self, _type, _value, _traceback):
+        pass
+
+    def _try_to_compile_and_link(self, source):
+        try:
+            with TemporaryDirectory() as work_dir:
+                src_file = 'check.c'
+                with open(os.path.join(work_dir, src_file), 'w') as handle:
+                    handle.write(source)
+
+                execute([self.ctx.compiler, src_file] + self.ctx.c_flags,
+                        cwd=work_dir)
+                return True
+        except Exception:
+            return False
+
+    def check_function_exists(self, function, name):
+        template = "int FUNCTION(); int main() { return FUNCTION(); }"
+        source = template.replace("FUNCTION", function)
+
+        logging.debug('Checking function %s', function)
+        found = self._try_to_compile_and_link(source)
+        logging.debug('Checking function %s -- %s', function,
+                      'found' if found else 'not found')
+        self.results.update({name: found})
+
+    def check_symbol_exists(self, symbol, include, name):
+        template = """#include <INCLUDE>
+                      int main() { return ((int*)(&SYMBOL))[0]; }"""
+        source = template.replace('INCLUDE', include).replace("SYMBOL", symbol)
+
+        logging.debug('Checking symbol %s', symbol)
+        found = self._try_to_compile_and_link(source)
+        logging.debug('Checking symbol %s -- %s', symbol,
+                      'found' if found else 'not found')
+        self.results.update({name: found})
+
+    def write_by_template(self, template, output):
+        def transform(line, definitions):
+
+            pattern = re.compile(r'^#cmakedefine\s+(\S+)')
+            m = pattern.match(line)
+            if m:
+                key = m.group(1)
+                if key not in definitions or not definitions[key]:
+                    return '/* #undef {} */\n'.format(key)
+                else:
+                    return '#define {}\n'.format(key)
+            return line
+
+        with open(template, 'r') as src_handle:
+            logging.debug('Writing config to %s', output)
+            with open(output, 'w') as dst_handle:
+                for line in src_handle:
+                    dst_handle.write(transform(line, self.results))
+
+
+class SharedLibrary:
+    def __init__(self, name, context):
+        self.name = context.shared_library_name(name)
+        self.ctx = context
+        self.inc = []
+        self.src = []
+        self.lib = []
+
+    def __enter__(self):
+        return self
+
+    def __exit__(self, _type, _value, _traceback):
+        pass
+
+    def add_include(self, directory):
+        self.inc.extend(['-I', directory])
+
+    def add_sources(self, source):
+        self.src.append(source)
+
+    def link_against(self, libraries):
+        self.lib.extend(['-l' + lib for lib in libraries])
+
+    def build_release(self, directory):
+        for src in self.src:
+            logging.debug('Compiling %s', src)
+            execute(
+                [self.ctx.compiler, '-c', os.path.join(self.ctx.src_dir, src),
+                 '-o', src + '.o'] + self.inc +
+                self.ctx.shared_library_c_flags(True),
+                cwd=directory)
+        logging.debug('Linking %s', self.name)
+        execute(
+            [self.ctx.compiler] + [src + '.o' for src in self.src] +
+            ['-o', self.name] + self.lib +
+            self.ctx.shared_library_ld_flags(True, self.name),
+            cwd=directory)
Index: tools/scan-build-py/libear/CMakeLists.txt
===================================================================
--- tools/scan-build-py/libear/CMakeLists.txt
+++ tools/scan-build-py/libear/CMakeLists.txt
@@ -29,10 +29,21 @@
 check_function_exists(posix_spawnp HAVE_POSIX_SPAWNP)
 check_symbol_exists(_NSGetEnviron crt_externs.h HAVE_NSGETENVIRON)
 
+find_package(Threads REQUIRED)
+
 configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h)
 include_directories(${CMAKE_CURRENT_BINARY_DIR})
 
 add_library(ear SHARED ear.c)
 target_link_libraries(ear ${CMAKE_DL_LIBS})
+if(THREADS_HAVE_PTHREAD_ARG)
+    set_property(TARGET ear PROPERTY COMPILE_OPTIONS "-pthread")
+    set_property(TARGET ear PROPERTY INTERFACE_COMPILE_OPTIONS "-pthread")
+endif()
+if(CMAKE_THREAD_LIBS_INIT)
+    target_link_libraries(ear "${CMAKE_THREAD_LIBS_INIT}")
+endif()
+
+set_target_properties(ear PROPERTIES INSTALL_RPATH "@loader_path/${CMAKE_INSTALL_LIBDIR}")
 
 install(TARGETS ear LIBRARY DESTINATION libscanbuild)
Index: tools/scan-build-py/README.md
===================================================================
--- tools/scan-build-py/README.md
+++ tools/scan-build-py/README.md
@@ -1,5 +1,4 @@
 [![Build Status](https://travis-ci.org/rizsotto/Beye.svg?branch=master)](https://travis-ci.org/rizsotto/Beye)
-[![Coverage Status](https://coveralls.io/repos/rizsotto/Beye/badge.svg?branch=master)](https://coveralls.io/r/rizsotto/Beye?branch=master)
 
 Build EYE
 =========
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to