diff --git a/compiledb/__init__.py b/compiledb/__init__.py index 2aab7e1..b3f5ae9 100644 --- a/compiledb/__init__.py +++ b/compiledb/__init__.py @@ -25,7 +25,7 @@ import logging from compiledb.parser import parse_build_log, Error - +from compiledb.utils import joined_native_pathname logger = logging.getLogger(__name__) @@ -45,13 +45,13 @@ def basename(stream): def generate_json_compdb(instream=None, proj_dir=os.getcwd(), exclude_files=[], add_predefined_macros=False, - use_full_path=False, command_style=False): + use_full_path=False, command_style=False, win_posix_shell=None): if not os.path.isdir(proj_dir): raise Error("Project dir '{}' does not exists!".format(proj_dir)) logger.info("## Processing build commands from {}".format(basename(instream))) result = parse_build_log(instream, proj_dir, exclude_files, add_predefined_macros=add_predefined_macros, - use_full_path=use_full_path, command_style=command_style) + use_full_path=use_full_path, command_style=command_style, win_posix_shell=win_posix_shell) return result @@ -83,7 +83,7 @@ def load_json_compdb(outstream): return [] -def merge_compdb(compdb, new_compdb, check_files=True): +def merge_compdb(compdb, new_compdb, check_files=True, win_posix_shell=None): def gen_key(entry): if 'directory' in entry: return os.path.join(entry['directory'], entry['file']) @@ -99,13 +99,14 @@ def check_file(path): def generate(infile, outfile, build_dir, exclude_files, overwrite=False, strict=False, - add_predefined_macros=False, use_full_path=False, command_style=False): + add_predefined_macros=False, use_full_path=False, command_style=False, + win_posix_shell=None): try: r = generate_json_compdb(infile, proj_dir=build_dir, exclude_files=exclude_files, add_predefined_macros=add_predefined_macros, use_full_path=use_full_path, - command_style=command_style) + command_style=command_style, win_posix_shell=win_posix_shell) compdb = [] if overwrite else load_json_compdb(outfile) - compdb = merge_compdb(compdb, r.compdb, strict) + compdb = merge_compdb(compdb, r.compdb, strict, win_posix_shell) write_json_compdb(compdb, outfile) logger.info("## Done.") return True diff --git a/compiledb/cli.py b/compiledb/cli.py index 481f2f4..a020b86 100644 --- a/compiledb/cli.py +++ b/compiledb/cli.py @@ -36,7 +36,8 @@ class Options(object): shared by all compiledb subcommands""" def __init__(self, infile, outfile, build_dir, exclude_files, no_build, - verbose, overwrite, strict, add_predefined_macros, use_full_path, command_style): + verbose, overwrite, strict, add_predefined_macros, use_full_path, command_style, + win_posix_shell): self.infile = infile self.outfile = outfile self.build_dir = build_dir @@ -48,6 +49,7 @@ def __init__(self, infile, outfile, build_dir, exclude_files, no_build, self.add_predefined_macros = add_predefined_macros self.use_full_path = use_full_path self.command_style = command_style + self.win_posix_shell = win_posix_shell @click.group(context_settings=CONTEXT_SETTINGS, invoke_without_command=True) @@ -79,9 +81,12 @@ def __init__(self, infile, outfile, build_dir, exclude_files, no_build, @click.option('--command-style', is_flag=True, default=False, help='Output compilation database with single "command" ' 'string rather than the default "arguments" list of strings.') +@click.option('--win-posix-shell', 'win_posix_shell', type=click.Choice(['msys', 'cygwin'], + case_sensitive=False), default=None, + help='Allowing for special pathname conventions of windows Posix shell envs.') @click.pass_context -def cli(ctx, infile, outfile, build_dir, exclude_files, no_build, verbose, overwrite, no_strict, add_predefined_macros, - use_full_path, command_style): +def cli(ctx, infile, outfile, build_dir, exclude_files, no_build, verbose, overwrite, + no_strict, add_predefined_macros, use_full_path, command_style, win_posix_shell): """Clang's Compilation Database generator for make-based build systems. When no subcommand is used it will parse build log/commands and generates its corresponding Compilation database.""" @@ -89,11 +94,11 @@ def cli(ctx, infile, outfile, build_dir, exclude_files, no_build, verbose, overw logging.basicConfig(level=log_level, format=None) if ctx.invoked_subcommand is None: done = generate(infile, outfile, build_dir, exclude_files, overwrite, not no_strict, add_predefined_macros, - use_full_path, command_style) + use_full_path, command_style, win_posix_shell) exit(0 if done else 1) else: ctx.obj = Options(infile, outfile, build_dir, exclude_files, no_build, verbose, overwrite, not no_strict, - add_predefined_macros, use_full_path, command_style) + add_predefined_macros, use_full_path, command_style, win_posix_shell) # Add subcommands diff --git a/compiledb/parser.py b/compiledb/parser.py index 9f4c550..09971c5 100755 --- a/compiledb/parser.py +++ b/compiledb/parser.py @@ -23,7 +23,7 @@ import logging from compiledb.compiler import get_compiler -from compiledb.utils import run_cmd +from compiledb.utils import run_cmd, to_native_pathname # Internal variables used to parse build log entries cc_compile_regex = re.compile(r"^.*-?g?cc-?[0-9.]*$|^.*-?clang-?[0-9.]*$") @@ -32,8 +32,8 @@ compiler_wrappers = {"ccache", "icecc", "sccache"} # Leverage `make --print-directory` option -make_enter_dir = re.compile(r"^\s*make\[\d+\]: Entering directory [`\'\"](?P.*)[`\'\"]\s*$") -make_leave_dir = re.compile(r"^\s*make\[\d+\]: Leaving directory .*$") +make_enter_dir = re.compile(r"^\s*make(\[\d+\])?: Entering directory [`\'\"](?P.*)[`\'\"]\s*$") +make_leave_dir = re.compile(r"^\s*make(\[\d+\])?: Leaving directory .*$") # We want to skip such lines from configure to avoid spurious MAKE expansion errors. checking_make = re.compile(r"^checking whether .* sets \$\(\w+\)\.\.\. (yes|no)$") @@ -60,7 +60,7 @@ def __str__(self): def parse_build_log(build_log, proj_dir, exclude_files, command_style=False, add_predefined_macros=False, - use_full_path=False, extra_wrappers=[]): + use_full_path=False, extra_wrappers=[], win_posix_shell=None): result = ParsingResult() def skip_line(cmd, reason): @@ -96,6 +96,7 @@ def skip_line(cmd, reason): enter_dir = make_enter_dir.match(line) if (make_enter_dir.match(line)): working_dir = enter_dir.group('dir') + working_dir = to_native_pathname(working_dir, win_posix_shell) dir_stack.append(working_dir) continue if (make_leave_dir.match(line)): @@ -156,13 +157,13 @@ def skip_line(cmd, reason): result.compdb.append({ 'directory': working_dir, 'command': command_str, - 'file': filepath, + 'file': to_native_pathname(filepath, win_posix_shell), }) else: result.compdb.append({ 'directory': working_dir, 'arguments': arguments, - 'file': filepath, + 'file': to_native_pathname(filepath, win_posix_shell), }) return result diff --git a/compiledb/utils.py b/compiledb/utils.py index 099cc39..f794725 100644 --- a/compiledb/utils.py +++ b/compiledb/utils.py @@ -1,5 +1,6 @@ import subprocess from sys import version_info +import os if version_info.major >= 3 and version_info.minor >= 6: def popen(cmd, encoding='utf-8', **kwargs): @@ -22,3 +23,25 @@ def run_cmd(cmd, encoding='utf-8', **kwargs): def cmd_join(cmd): return ' '.join(cmd_quote(s) for s in cmd) + + +_CYGDRIVE_PREFIX = "/cygdrive/" +_LEN_CYGDRIVE_PREFIX = len(_CYGDRIVE_PREFIX) + + +def to_native_pathname(pathname, win_posix_shell): + if win_posix_shell == "msys": + # MSYS drive-letter convention. + if len(pathname) > 2 and pathname[0] == '/' and pathname[2] == '/': + return pathname[1] + ':' + pathname[2:] + elif win_posix_shell == "cygwin": + # Cygwin "/cygdrive" convention + if pathname.startswith(_CYGDRIVE_PREFIX): + return pathname[_LEN_CYGDRIVE_PREFIX+1] + ':' + pathname[_LEN_CYGDRIVE_PREFIX+2:] + else: + return pathname + + +def joined_native_pathname(dir_pathname, pathname, win_posix_shell): + pathname = to_native_pathname(pathname, win_posix_shell) + return os.path.join(dir_pathname, pathname)