facebook / buck2

Build system, successor to Buck
https://buck2.build/
Apache License 2.0
3.58k stars 221 forks source link

compile_command.json write to root directory #718

Open pollend opened 3 months ago

pollend commented 3 months ago

Is it possible to have a rule write to the root directory of the project? i can grab the file and copy it back to the root but is there a better way to do this?

genrule(
  name = 'compile_commands',
  out  = 'compile_commands.json',
  cmd  = 'cp $(location //:TF[full-compilation-database]) $OUT'
)
cbarrete commented 3 months ago

You can't have a rule write outside of buck-out (without hacks anyway), but you can do something like buck2 build //my:target[full-compilation-database] --out ..

Unsollicited tip: --out - will print the contents of the output to stdout instead.

pollend commented 3 months ago

that's annoying. I couldn't get full-compilation-database to work with clangd. I ended up writing this bxl script so i can generate the correct commands for clangd.

cat $(buck2 bxl //compile_command.bxl:gen_compile_command -- --base_dir $(pwd))  > compile_commands.json
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under both the MIT license found in the
# LICENSE-MIT file in the root directory of this source tree and the Apache
# License, Version 2.0 found in the LICENSE-APACHE file in the root directory
# of this source tree.
load(
    "@prelude//cxx:comp_db.bzl",
    "CxxCompilationDbInfo",
)
load("@prelude//:paths.bzl", "paths")
load("@prelude//paths.bzl", "paths")

def _gen_compile_command(ctx: BxlContext) -> None:
    target_filter = ctx.cli_args.targets or "..."
    targets = ctx.configured_targets(target_filter, target_platform = ctx.cli_args.platform)

    entries = [] 
    infos = {}

    for target in targets:
        providers = ctx.analysis(target).providers()
        compile_db_info = providers.get(CxxCompilationDbInfo)
        if compile_db_info != None:
            infos |= compile_db_info.info
    for key in infos:
        cmd_entry = infos[key]
        entry = {}
        entry["directory"] = ctx.cli_args.base_dir 
        entry["file"] = cmd_entry.src.short_path
        cxx = cmd_entry.cxx_compile_cmd
        if cxx:
            entry["command"] = cmd_args(cxx.base_compile_cmd, cxx.argsfile.input_args[0], cmd_entry.args, delimiter = " ")

        entries.append(entry)

    actions = ctx.bxl_actions(target_platform = ctx.cli_args.platform).actions
    db_artifact = actions.write_json("compile_commands.json", entries)
    db_artifact_ensured = ctx.output.ensure(db_artifact)
    ctx.output.print(db_artifact_ensured)

gen_compile_command = bxl_main(
    impl = _gen_compile_command,
    cli_args = {
        "base_dir": cli_args.option(cli_args.string()),
        "targets": cli_args.option(cli_args.target_expr()),
        "platform": cli_args.option(cli_args.target_label())
    },
)
cbarrete commented 3 months ago

Right, this is something which I've also hit and reported at https://github.com/facebook/buck2/issues/307. I personally worked around it by forking Meta's compile commands generation script and getting it to output absolute paths instead, so that my users don't have to call/learn about BXL.

It's really unfortunate that the defaults are just broken though, every new user is going to hit this. I really wish that Meta would change the defaults and opt into relative paths internally. Please chime in the other issue if you agree!

pollend commented 3 months ago

here is what i do for windows.

$CommandPath = buck2 bxl //compile_command.bxl:gen_compile_command -- --base_dir (Get-Location).Path
cat $CommandPath | Out-File compile_commands.json
zjturner commented 3 months ago

I have a PR up that is -- imo anyway -- a better and faster compile_commands generator bxl. Note that it's easy to put outputs anywhere you want if you invoke python from the bxl, perhaps via an anonymous action. The python script can reach out of the sandbox, and you could (for example) pass it a path to the json as well as a path to the desired output location, and the python script can just copy it there.

zjturner commented 3 months ago

https://github.com/facebook/buck2/pull/510

pollend commented 2 months ago

I've just been updating this script for my own use.

# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under both the MIT license found in the
# LICENSE-MIT file in the root directory of this source tree and the Apache
# License, Version 2.0 found in the LICENSE-APACHE file in the root directory
# of this source tree.
load(
    "@prelude//cxx:comp_db.bzl",
    "CxxCompilationDbInfo",
)
load("@prelude//paths.bzl", "paths")

def _gen_compile_command(ctx: BxlContext) -> None:
    entries = [] 
    query_targets = {}
    for filter in ctx.cli_args.filter:
        targets = ctx.configured_targets(filter, target_platform = ctx.cli_args.platform)
        for target in targets:
            query_targets[target.label.raw_target()] = target 

    for key in query_targets:
        target = query_targets[key]

        providers = ctx.analysis(target).providers()
        compile_db_info = providers.get(CxxCompilationDbInfo)

        if compile_db_info != None:
            for key in compile_db_info.info:
                cmd_entry = compile_db_info.info[key]
                entry = {}
                entry["directory"] = ctx.fs.abs_path_unsafe("root//") #"{}".format(target.label.path))
                entry["file"] = cmd_entry.src.short_path
                cxx = cmd_entry.cxx_compile_cmd
                if cxx:
                    entry["command"] = cmd_args(cxx.base_compile_cmd, cxx.argsfile.input_args[0], cmd_entry.args, delimiter = " ")
                entries.append(entry)

    actions = ctx.bxl_actions(target_platform = ctx.cli_args.platform).actions
    db_artifact = actions.write_json("compile_commands.json", entries)
    db_artifact_ensured = ctx.output.ensure(db_artifact)
    ctx.output.print(db_artifact_ensured)

gen_compile_command = bxl_main(
    impl = _gen_compile_command,
    cli_args = {
        "filter": cli_args.list(cli_args.target_expr()),
        "platform": cli_args.option(cli_args.target_label())
    },
)