From 648ade39244f40be4eba7db1b5d2e45e95663986 Mon Sep 17 00:00:00 2001 From: Overhatted <15021741+Overhatted@users.noreply.github.com> Date: Wed, 27 Dec 2023 19:41:24 +0000 Subject: [PATCH 1/3] Made default platform public --- prelude/platforms/BUCK.v2 | 1 + 1 file changed, 1 insertion(+) diff --git a/prelude/platforms/BUCK.v2 b/prelude/platforms/BUCK.v2 index 6e45ed1d2d2f2..d10b161f8c2dc 100644 --- a/prelude/platforms/BUCK.v2 +++ b/prelude/platforms/BUCK.v2 @@ -9,6 +9,7 @@ execution_platform( cpu_configuration = host_configuration.cpu, os_configuration = host_configuration.os, use_windows_path_separators = host_info().os.is_windows, + visibility = ["PUBLIC"], ) prelude.constraint_setting( From 9f5c06fcdcf1715b10147465a1105493b8e59f03 Mon Sep 17 00:00:00 2001 From: Overhatted <15021741+Overhatted@users.noreply.github.com> Date: Sat, 10 Feb 2024 13:23:54 +0000 Subject: [PATCH 2/3] Removed "msvcrt.lib" from the linker flags on C++ Windows This makes it impossible to choose the debug version of the run-time library, such as with /MDd for example. Even without any of the /MD, /MT or /LD options the linker doesn't seem to complain about missing symbols, presumably because the compiler defaults to one of these options. --- prelude/toolchains/cxx.bzl | 1 - 1 file changed, 1 deletion(-) diff --git a/prelude/toolchains/cxx.bzl b/prelude/toolchains/cxx.bzl index 8863bce95b4f5..682ea8ca302ea 100644 --- a/prelude/toolchains/cxx.bzl +++ b/prelude/toolchains/cxx.bzl @@ -67,7 +67,6 @@ def _system_cxx_toolchain_impl(ctx: AnalysisContext): shared_library_name_default_prefix = "" shared_library_name_format = "{}.dll" shared_library_versioned_name_format = "{}.dll" - additional_linker_flags = ["msvcrt.lib"] pic_behavior = PicBehavior("not_supported") elif ctx.attrs.linker == "g++" or ctx.attrs.cxx_compiler == "g++": pass From 79cec6c682eb5acc139bb2096b9a6459f3419744 Mon Sep 17 00:00:00 2001 From: Overhatted <15021741+Overhatted@users.noreply.github.com> Date: Fri, 15 Dec 2023 15:41:49 +0000 Subject: [PATCH 3/3] Added example using Buck with Visual Studio Code on Windows --- examples/visual_studio/.buckconfig | 17 +++ examples/visual_studio/.buckroot | 0 examples/visual_studio/.gitignore | 4 + .../visual_studio/.vscode/extensions.json | 3 + examples/visual_studio/.vscode/launch.json | 13 ++ examples/visual_studio/.vscode/settings.json | 4 + examples/visual_studio/.vscode/tasks.json | 55 ++++++++ examples/visual_studio/BUCK | 15 ++ examples/visual_studio/README.md | 8 ++ .../buck2_utils/configuration/BUCK | 16 +++ examples/visual_studio/buck2_utils/copy.ps1 | 5 + .../buck2_utils/create_compile_commands.bxl | 133 ++++++++++++++++++ .../visual_studio/buck2_utils/install.ps1 | 69 +++++++++ examples/visual_studio/library.cpp | 16 +++ examples/visual_studio/library.hpp | 25 ++++ examples/visual_studio/main.c | 14 ++ examples/visual_studio/toolchains/BUCK | 56 ++++++++ 17 files changed, 453 insertions(+) create mode 100644 examples/visual_studio/.buckconfig create mode 100644 examples/visual_studio/.buckroot create mode 100644 examples/visual_studio/.gitignore create mode 100644 examples/visual_studio/.vscode/extensions.json create mode 100644 examples/visual_studio/.vscode/launch.json create mode 100644 examples/visual_studio/.vscode/settings.json create mode 100644 examples/visual_studio/.vscode/tasks.json create mode 100644 examples/visual_studio/BUCK create mode 100644 examples/visual_studio/README.md create mode 100644 examples/visual_studio/buck2_utils/configuration/BUCK create mode 100644 examples/visual_studio/buck2_utils/copy.ps1 create mode 100644 examples/visual_studio/buck2_utils/create_compile_commands.bxl create mode 100644 examples/visual_studio/buck2_utils/install.ps1 create mode 100644 examples/visual_studio/library.cpp create mode 100644 examples/visual_studio/library.hpp create mode 100644 examples/visual_studio/main.c create mode 100644 examples/visual_studio/toolchains/BUCK diff --git a/examples/visual_studio/.buckconfig b/examples/visual_studio/.buckconfig new file mode 100644 index 0000000000000..668290682e9c6 --- /dev/null +++ b/examples/visual_studio/.buckconfig @@ -0,0 +1,17 @@ +[repositories] +root = . +prelude = prelude +toolchains = toolchains +none = none + +[repository_aliases] +config = prelude +fbcode = none +fbsource = none +buck = none + +[parser] +target_platform_detector_spec = target:root//...->toolchains//:default + +[build] +execution_platforms = prelude//platforms:default diff --git a/examples/visual_studio/.buckroot b/examples/visual_studio/.buckroot new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/examples/visual_studio/.gitignore b/examples/visual_studio/.gitignore new file mode 100644 index 0000000000000..714e9028dc05f --- /dev/null +++ b/examples/visual_studio/.gitignore @@ -0,0 +1,4 @@ +/buck-out +/compile_commands.json +/install +/.cache diff --git a/examples/visual_studio/.vscode/extensions.json b/examples/visual_studio/.vscode/extensions.json new file mode 100644 index 0000000000000..b8158f146e2e5 --- /dev/null +++ b/examples/visual_studio/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["llvm-vs-code-extensions.vscode-clangd", "vadimcn.vscode-lldb"] +} diff --git a/examples/visual_studio/.vscode/launch.json b/examples/visual_studio/.vscode/launch.json new file mode 100644 index 0000000000000..e7ea8093a2fb3 --- /dev/null +++ b/examples/visual_studio/.vscode/launch.json @@ -0,0 +1,13 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "type": "lldb", + "request": "launch", + "name": "Debug main.exe", + "preLaunchTask": "Build and create compilation database", + "program": "${workspaceFolder}/install/main.exe", + "cwd": "${workspaceFolder}/install" + } + ] +} \ No newline at end of file diff --git a/examples/visual_studio/.vscode/settings.json b/examples/visual_studio/.vscode/settings.json new file mode 100644 index 0000000000000..3868e33919f46 --- /dev/null +++ b/examples/visual_studio/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "C_Cpp.intelliSenseEngine": "disabled", + "search.followSymlinks": false +} \ No newline at end of file diff --git a/examples/visual_studio/.vscode/tasks.json b/examples/visual_studio/.vscode/tasks.json new file mode 100644 index 0000000000000..37e2fd4d8b7e7 --- /dev/null +++ b/examples/visual_studio/.vscode/tasks.json @@ -0,0 +1,55 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Build Main", + "type": "shell", + "command": "buck2", + "args": [ + "build", + "--console", + "simple", + "--no-interactive-console", + "--fail-fast", + "--show-full-output", + ":main", + "|", + "powershell", + "-ExecutionPolicy", + "Bypass", + "-File", + "buck2_utils/install.ps1", + "install" + ] + }, + { + "label": "Create compilation database", + "type": "shell", + "command": "buck2", + "args": [ + "bxl", + "buck2_utils/create_compile_commands.bxl:gen_compilation_database", + "--", + "--directory", + "${workspaceFolder}", + "|", + "powershell", + "-ExecutionPolicy", + "Bypass", + "-File", + "buck2_utils/copy.ps1" + ] + }, + { + "label": "Build and create compilation database", + "dependsOn": [ + "Create compilation database", + "Build Main" + ], + "group": { + "kind": "build", + "isDefault": true + } + } + ] +} \ No newline at end of file diff --git a/examples/visual_studio/BUCK b/examples/visual_studio/BUCK new file mode 100644 index 0000000000000..22ef664520d75 --- /dev/null +++ b/examples/visual_studio/BUCK @@ -0,0 +1,15 @@ +cxx_binary( + name = "main", + srcs = ["main.c"], + link_style = "shared", + deps = [":print"], + resources = [":print[shared]"] +) + +cxx_library( + name = "print", + srcs = ["library.cpp"], + exported_headers = glob(["**/*.hpp"]), + preprocessor_flags = ["-DLIBRARY_EXPORT"], + visibility = ["PUBLIC"], +) diff --git a/examples/visual_studio/README.md b/examples/visual_studio/README.md new file mode 100644 index 0000000000000..77f6e8e430ec6 --- /dev/null +++ b/examples/visual_studio/README.md @@ -0,0 +1,8 @@ +## An example showing how Buck2 can be used in Visual Studio Code on Windows + +After completing the setup, below, click F5 to run with the debugger attached. You can also use Ctrl + Shift + B to build. By default it compiles with the "debug" configuration. To compile in release pass "-c release" to Buck2's build command. + +## Setup + +Run `buck2 init --git`. +Open this folder in Visual Studio Code and install the recommended extensions. diff --git a/examples/visual_studio/buck2_utils/configuration/BUCK b/examples/visual_studio/buck2_utils/configuration/BUCK new file mode 100644 index 0000000000000..8c4dcb243e5a8 --- /dev/null +++ b/examples/visual_studio/buck2_utils/configuration/BUCK @@ -0,0 +1,16 @@ +constraint_setting( + name = "configuration", + visibility = ["PUBLIC"], +) + +constraint_value( + name = "debug", + constraint_setting = ":configuration", + visibility = ["PUBLIC"], +) + +constraint_value( + name = "release", + constraint_setting = ":configuration", + visibility = ["PUBLIC"], +) diff --git a/examples/visual_studio/buck2_utils/copy.ps1 b/examples/visual_studio/buck2_utils/copy.ps1 new file mode 100644 index 0000000000000..76bb52385a250 --- /dev/null +++ b/examples/visual_studio/buck2_utils/copy.ps1 @@ -0,0 +1,5 @@ +param( + [parameter(Mandatory=$true)] [String] $FilePath +) + +New-Item -ItemType HardLink -Force -Path compile_commands.json -Target $FilePath.Trim() | Out-Null diff --git a/examples/visual_studio/buck2_utils/create_compile_commands.bxl b/examples/visual_studio/buck2_utils/create_compile_commands.bxl new file mode 100644 index 0000000000000..b311d53fe6070 --- /dev/null +++ b/examples/visual_studio/buck2_utils/create_compile_commands.bxl @@ -0,0 +1,133 @@ +load("@prelude//:paths.bzl", "paths") + +def group_by_category(actions): + groups = {} + for a in actions: + category = a.attrs.category.value() + if category in groups: + groups[category].append(a) + else: + groups[category] = [a] + return groups + +def create_compilation_database_entry(directory, buildfile_folder, argsfiles, action): + # the "cmd" attribute looks like a list. For example, it might be something like: + # cmd = [cl.exe, -c, foo.cpp] + # but in reality this is quite literally the string "[cl.exe, -c, foo.cpp]". We need + # it as an actual list, so we have to employ this unfortunate hack of stripping the brackets, + # splitting on comma operator, and then stripping spaces from each entry to make an actual + # list. Hopefully this doesn't fail in any weird edge cases, but it seems to be the best we + # can do until buck2 upstream actually stores a Starlark list as the value of this attribute. + cmd = action.attrs.cmd.value() + cmd = cmd.strip("[]").split(',') + cmd = list(map(lambda x: x.strip(), cmd)) + + compiler = cmd[0].replace(".bat", ".exe") + + # Find the argsfile. On Windows it might contain backslashes, so convert those to forward + # slashes. Hopefully we don't have any filenames with actual backslashes in them. Then + # strip off the directory name so we just have the filename of the argsfile. + argsfile = filter(lambda x: x.startswith("@"), cmd)[0][1:] + identifier = paths.basename(argsfile.replace("\\", "/")) + + # Get the path of the source file relative to the project root by appending the identifier + # to the directory of the BUCK file. + project_rel_path = paths.join(buildfile_folder, action.attrs.identifier.value()) + + # Using the previously computed list of argfiles referenced by this target, find the one + # that this particular action references. + arguments = [] + if identifier in argsfiles: + arguments = argsfiles[identifier] + + # Now build the compilation database entry + entry = { + "file": project_rel_path, + "directory": directory, + "arguments": [compiler] + arguments + } + return entry + +def gen_compilation_database_impl(ctx): + # Generate compilation database for all targets unless user requests a more narrow set + target_filter = ctx.cli_args.targets or '...' + targets = ctx.configured_targets(target_filter, target_platform=ctx.cli_args.platform) + aquery = ctx.aquery() + + entries = {} + for target in targets: + target_actions = list(aquery.all_actions(target.label)) + + # The individual compilation actions only identify the source files relative to where + # the BUCK file is. So to build a path relative to the root of the project, we need + # to get the BUCK file path and append the source file path to it. + buildfile_path = ctx.fs.project_rel_path(target.buildfile_path) + buildfile_folder = paths.dirname(buildfile_path) + + # In order to generate a compilation database entry for a file, we need this target + # to satisfy two conditions: + # 1. It has compilations + # 2. It writes an argsfile. + # + # The second requirement is necessary because the argsfile is where buck2 includes + # important command line arguments such as preprocessor definitions and include + # paths. If both conditions are not satisfied, we should skip this target because + # either it doesn't have source files anyway, or we don't understand how to write + # compilation database entries for them. + actions_by_category = group_by_category(target_actions) + + if not "write" in actions_by_category: + continue + if not "cxx_compile" in actions_by_category: + continue + + argsfiles = {} + + # There are lots of kinds of "write" actions, but we are interested specifically in argsfile + # write actions. This is because the source file compilation actions will reference them, + # so for each source file we need to map it back to the argsfile that its command line references + # so we can write those arguments to the compilation database entry. Build this mapping here. + for write_action in actions_by_category["write"]: + identifier = write_action.attrs.identifier.value() + if identifier.endswith(".argsfile"): + # The content of the argsfile is a newline separated list. We need a Starlark list. So + # split on newline. + arguments = write_action.attrs.contents.value() + arguments = arguments.split("\n") + + # For whatever reason, there are a lot of unnecessary quotes and double slashes. Probably + # something to do with the nature of the tools requiring shell-escaped values. Since we're + # storing this in a JSON array and the tool that processes the compilation database does its + # own escaping of these values, we can undo all of this. Convert double backslash to + # single forward slash, and remove quotes at the beginning and end of entries. + arguments = list(map(lambda x: x.strip('"').replace("\\\\", "/"), arguments)) + argsfiles[identifier] = arguments + + # Now walk each compilation action and generate a compilation database entry for it. + for action in actions_by_category["cxx_compile"]: + entry = create_compilation_database_entry(ctx.cli_args.directory, buildfile_folder, argsfiles, action) + is_pic = entry["file"].endswith(" (pic)") + file_name = entry["file"].removesuffix(" (pic)") + entry["file"] = file_name + entry["is_pic"] = is_pic + existing_entry = entries.get(file_name) + if existing_entry == None or existing_entry["is_pic"]: + entries[file_name] = entry + + for entry in entries.values(): + entry.pop("is_pic") + + actions = ctx.bxl_actions(target_platform = ctx.cli_args.platform).actions + db_artifact = actions.write_json("compile_commands.json", entries.values()) + db_artifact_ensured = ctx.output.ensure(db_artifact) + + ctx.output.print(db_artifact_ensured) + +gen_compilation_database = bxl_main( + impl = gen_compilation_database_impl, + cli_args = { + "directory": cli_args.string(), + "targets": cli_args.option(cli_args.target_expr()), + "platform": cli_args.option(cli_args.target_label()) + } +) diff --git a/examples/visual_studio/buck2_utils/install.ps1 b/examples/visual_studio/buck2_utils/install.ps1 new file mode 100644 index 0000000000000..42c1979fc9963 --- /dev/null +++ b/examples/visual_studio/buck2_utils/install.ps1 @@ -0,0 +1,69 @@ +param( + [parameter(Mandatory=$true)] [String] $InstallDirectory, + [parameter(Mandatory=$true,ValueFromPipeline=$true)] [String] $Buck2Output +) + +begin +{ + $ErrorActionPreference = "Stop" + + function Copy-Directory + { + param( + [parameter(Mandatory=$true)] [String] $SourcePath, + [parameter(Mandatory=$true)] [String] $TargetPath + ) + robocopy /MIR "$SourcePath" "$TargetPath" | Out-Null + } + + function Copy-File + { + param( + [parameter(Mandatory=$true)] [String] $SourcePath, + [parameter(Mandatory=$true)] [String] $TargetPath + ) + Copy-Item $SourcePath -Destination $TargetPath + } +} +process +{ + $OutputSplit = $input.Split(" ") + $TargetPath = $OutputSplit[0].Trim() + $ExecutablePath = $OutputSplit[1].Trim() + $ResourcesPath = $ExecutablePath + ".resources.json" + + $CopySource = $ExecutablePath + $CopyTarget = Join-Path -Path $InstallDirectory -ChildPath (Split-Path -Leaf $ExecutablePath) + Copy-File $CopySource $CopyTarget + + try + { + $ResourcesContent = Get-Content $ResourcesPath -ErrorAction Stop | ConvertFrom-Json + $HasResources = $true + } + catch [System.Management.Automation.ItemNotFoundException] + { + $ResourcesContent = @() + $HasResources = $false + } + if($HasResources) + { + foreach($ResourceProperty in $ResourcesContent.PSObject.Properties) + { + $ResourceTargetPath = $ResourceProperty.Name + $ResourceSourcePath = $ResourceProperty.Value + $CopySource = Join-Path -Path (Split-Path -Parent $ExecutablePath) -ChildPath $ResourceSourcePath + $CopyTarget = Join-Path -Path $InstallDirectory -ChildPath $ResourceTargetPath + + $GetItemResult = Get-Item $CopySource + if($GetItemResult.PSIsContainer) + { + Copy-Directory $CopySource $CopyTarget + } + else + { + Copy-File $CopySource $CopyTarget + } + } + } +} diff --git a/examples/visual_studio/library.cpp b/examples/visual_studio/library.cpp new file mode 100644 index 0000000000000..c833fce36ea7e --- /dev/null +++ b/examples/visual_studio/library.cpp @@ -0,0 +1,16 @@ +/* + * 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. + */ + +#include "library.hpp" + +#include + +void print_hello() { + std::cout << "Hello from a C++ Buck2 program!" << std::endl; +} diff --git a/examples/visual_studio/library.hpp b/examples/visual_studio/library.hpp new file mode 100644 index 0000000000000..99af7f9c578a4 --- /dev/null +++ b/examples/visual_studio/library.hpp @@ -0,0 +1,25 @@ +/* + * 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. + */ + +#ifdef _WIN32 +#ifdef LIBRARY_EXPORT +#define DLL_API __declspec(dllexport) +#else +#define DLL_API __declspec(dllimport) +#endif +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif +DLL_API void print_hello(); +#ifdef __cplusplus +} +#endif diff --git a/examples/visual_studio/main.c b/examples/visual_studio/main.c new file mode 100644 index 0000000000000..05915f3aa13cd --- /dev/null +++ b/examples/visual_studio/main.c @@ -0,0 +1,14 @@ +/* + * 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. + */ + +#include "library.hpp" + +int main() { + print_hello(); +} diff --git a/examples/visual_studio/toolchains/BUCK b/examples/visual_studio/toolchains/BUCK new file mode 100644 index 0000000000000..215f793c0e4aa --- /dev/null +++ b/examples/visual_studio/toolchains/BUCK @@ -0,0 +1,56 @@ +load("@prelude//toolchains:cxx.bzl", "system_cxx_toolchain") +load("@prelude//toolchains:python.bzl", "system_python_bootstrap_toolchain") + +system_cxx_toolchain( + name = "cxx", + visibility = ["PUBLIC"], + link_style = "static", + #The flags in the below attributes' Windows config are copied from Visual Studio's project "Console App", with some changes listed in each attribute + c_flags = select({ + #Copied from the C++ flags + "config//os:windows": ["/c", "/Z7", "/nologo", "/W3", "/WX-", "/diagnostics:column", "/sdl", "/D_CONSOLE", "/D_UNICODE", "/DUNICODE", "/EHsc", "/Zc:forScope", "/Zc:inline", "/permissive-", "/TC"] + select({ + "root//buck2_utils/configuration:debug": ["/Od", "/D_DEBUG", "/RTC1", "/MDd"], + "root//buck2_utils/configuration:release": ["/O2", "/Oi", "/GL", "/DNDEBUG", "/MD", "/Gy"], + }), + "config//os:linux": [], + }), + cxx_flags = select({ + #Added C++ 20 version flag + #Removed flags that are already the default: /GS, /fp:precise /Gd /Zc:wchar_t /Gm- + #Removed deprecated /errorReport:prompt + #Converted /ZI to /Z7 because I'm guessing /ZI won't play very well with remote execution + #Removed /FC because it probably doesn't work with remote execution + #Removed /external:W3 because it's redundant + #Removed /JMC because it doesn't work with lldb + "config//os:windows": ["/std:c++20", "/c", "/Z7", "/nologo", "/W3", "/WX-", "/diagnostics:column", "/sdl", "/D_CONSOLE", "/D_UNICODE", "/DUNICODE", "/EHsc", "/Zc:forScope", "/Zc:inline", "/permissive-", "/TP"] + select({ + "root//buck2_utils/configuration:debug": ["/Od", "/D_DEBUG", "/RTC1", "/MDd"], + "root//buck2_utils/configuration:release": ["/O2", "/Oi", "/GL", "/DNDEBUG", "/MD", "/Gy"], + }), + "config//os:linux": ["-std=c++20"], + }), + link_flags = select({ + #Removed flags that are already the default: /TLBID:1 /DYNAMICBASE /NXCOMPAT /ERRORREPORT:PROMPT /MANIFEST /MANIFESTUAC:"level='asInvoker' uiAccess='false'" /SUBSYSTEM:CONSOLE + #Removed /manifest:embed because I don't know what it does + #Removed default extra libs that are unnecessary most of the time: kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib + #Added /INCREMENTAL:NO to force non-incremental mode + "config//os:windows": ["/NOLOGO", "/DEBUG", "/INCREMENTAL:NO"] + select({ + "root//buck2_utils/configuration:debug": [], + #Converted /LTCG:incremental to /LTCG because I'm guessing incremental actions won't work well with Buck2 + "root//buck2_utils/configuration:release": ["/OPT:REF", "/OPT:ICF", "/LTCG"], + }), + "config//os:linux": [], + }), +) + +system_python_bootstrap_toolchain( + name = "python_bootstrap", + visibility = ["PUBLIC"], +) + +configuration = read_config("cxx", "configuration", "debug") + +platform( + name = "default", + deps = ["prelude//platforms:default"], + constraint_values = ["root//buck2_utils/configuration:release"] if configuration == "release" else ["root//buck2_utils/configuration:debug"], +)