Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 4 additions & 11 deletions cfg_constructor_if_standalone.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,10 @@ load(
load("@prelude//cfg/modifier/common.bzl", "MODIFIER_METADATA_KEY")

def cfg_constructor_if_standalone():
# get_cell_name() gives the name of *this* PACKAGE file's own cell -
# "root" when this checkout's own .buckconfig is the outermost one
# (genuinely standalone), or "hsthrift" when nested inside another
# project's cell graph (see buck2.md's hsthrift-cell entry for why
# cell names are global to whichever project is outermost - that's
# exactly what makes this check correct: a plain read_config() flag
# in this project's own .buckconfig does *not* work here, since a
# cell's own .buckconfig is still consulted for ordinary config
# values even when nested, unlike the special-cased [cells] section -
# confirmed directly, the first version of this check used that and
# it read as true (and hard-errored the nested build) either way.
# get_cell_name() gives the name of *this* PACKAGE file's own cell
# - "root" when this checkout's own .buckconfig is the outermost
# one (genuinely standalone), or the name of the cell when nested
# inside another project's cell graph.
if get_cell_name() != "root":
return

Expand Down
62 changes: 30 additions & 32 deletions haskell.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,21 @@ def _package_deps(packages):
all_pkgs = {p: None for p in (AUTO_PACKAGES + packages)}
return [("@third-party-haskell//:" + p) for p in sorted(all_pkgs.keys())]

# Build modes (buck2.md TODO "we should support different build modes"),
# selected via `buck2 build ... -m root//buck2/constraints:opt` (`dev` is
# the default - see the root PACKAGE file). `dev` matches this migration's
# original, only behaviour (shared libs, no optimisation - fast to
# rebuild); `opt` is what an actual deployed `glean` binary wants (a single
# static binary, optimised). Centralized here rather than passed by each
# BUCK file, the same reasoning as FB_HASKELL_EXTENSIONS above - one place
def _cabal_macros_include_flags(cabal_component):
if cabal_component == None:
return []
pkg, component = cabal_component
autogen_dir = "cabal-buck2/autogen" if pkg == "." else pkg + "/cabal-buck2/autogen"
label = "//" + autogen_dir + ":" + component + "-cabal-macros"
return ["-optP-include", "-optP$(location " + label + ")"]

# Build modes selected via `buck2 build ... -m
# root//buck2/constraints:opt` (`dev` is the default - see the root
# PACKAGE file). `dev` matches this migration's original, only
# behaviour (shared libs, no optimisation - fast to rebuild); `opt` is
# what an actual deployed `glean` binary wants (a single static
# binary, optimised). Centralized here rather than passed by each BUCK
# file, the same reasoning as FB_HASKELL_EXTENSIONS above - one place
# to change, automatically applied to every haskell_library()/
# haskell_binary() in the tree.
#
Expand Down Expand Up @@ -102,17 +110,8 @@ _BUILD_MODE_PREFERRED_LINKAGE = select({
}),
})

# The `-dynamic-too`/native-shared-libs-symlink-tree machinery this
# section used to force on unconditionally (via a `dynamic_too = True`
# kwarg here) is now driven entirely by `haskell_toolchain.dynamic_ghc`
# instead - a LOCAL FORK in `buck2/prelude/haskell/{toolchain,haskell}
# .bzl` (see buck2.md) auto-detected per GHC installation by buck2/
# gen-haskell-prebuilt.py (`ghc --info`'s own `("GHC Dynamic",...)`
# report), not hardcoded here. No kwarg needed any more - the prelude's
# own `haskell_library_impl`/`haskell_binary_impl` read the toolchain
# directly. Kept for reference, since the *reasoning* for why this
# needs to be on whenever GHC itself is dynamically linked is still the
# same as when this was a hardcoded `True`:
# Why the toolchain needs to know whether GHC was linked dynamically
# (dynamic_ghc):
#
# 1. Template Haskell splices need every package loadable the *dynamic*
# way: a dynamically-linked `ghc` binary's internal splice
Expand Down Expand Up @@ -187,11 +186,7 @@ _PROF_ENABLED = select({
"DEFAULT": False,
})

# Matches glean.cabal.in's `common exe`: `if flag(asan) ghc-options:
# -optc-fsanitize=address -optl-fsanitize=address` - only executables
# (haskell_binary(), below) link a C/C++ runtime that asan instruments,
# so (matching Cabal's own `common exe`, never applied to a library
# component) this is only added there, not in haskell_library().
# Linker flags enabled when building with ASAN
_ASAN_LINKER_FLAGS = select({
"root//buck2/constraints:asan": ["-optc-fsanitize=address", "-optl-fsanitize=address"],
"DEFAULT": [],
Expand Down Expand Up @@ -278,6 +273,13 @@ def haskell_library(
# hsc2hs.bzl's `extra_flags`) - the buck2 equivalent of Cabal's
# per-library `hsc2hs-options` field.
hsc_flags = [],
# `cabal_component = (pkg, component)` causes this component's
# `cabal_macros.h` file to be included when `{-# LANGUAGE CPP #-}`
# is on, which provides access to the MIN_VERSION_pkg(x,y,z) macros
# amongst other things. However, GHC also provides the MIN_VERSION
# macros by default, so unless you need anything else from
# cabal_macros.h there's no need to use this.
cabal_component = None,
**kwargs):
# No `link_style` here - unlike haskell_binary(), haskell_library()
# doesn't take one at all: a library builds whichever output styles its
Expand All @@ -293,7 +295,7 @@ def haskell_library(
native.haskell_library(
name = name,
srcs = _resolve_srcs(name, srcs, all_deps, hsc_flags),
compiler_flags = compiler_flags + _BUILD_MODE_HASKELL_FLAGS,
compiler_flags = compiler_flags + _BUILD_MODE_HASKELL_FLAGS + _cabal_macros_include_flags(cabal_component),
deps = all_deps,
**kwargs
)
Expand All @@ -306,6 +308,7 @@ def haskell_binary(
compiler_flags = [],
hsc_flags = [],
linker_flags = [],
cabal_component = None, # see haskell_library()
**kwargs):
all_deps = deps + _package_deps(packages)
kwargs.setdefault("link_style", _BUILD_MODE_LINK_STYLE)
Expand All @@ -323,7 +326,7 @@ def haskell_binary(
# (`-optl-...` during a compile-only invocation, `-optc-...` when
# nothing needs the C compiler), so adding both everywhere is the
# faithful equivalent, not redundant belt-and-braces.
compiler_flags = compiler_flags + _BUILD_MODE_HASKELL_FLAGS + _ASAN_LINKER_FLAGS,
compiler_flags = compiler_flags + _BUILD_MODE_HASKELL_FLAGS + _ASAN_LINKER_FLAGS + _cabal_macros_include_flags(cabal_component),
deps = all_deps,
linker_flags = _ASAN_LINKER_FLAGS + linker_flags,
**kwargs
Expand All @@ -334,19 +337,14 @@ def haskell_binary(
# needs nothing Haskell-specific: this builds the exact same
# haskell_binary() `name` would (so `buck2 run :name` is unaffected), plus
# a same-named `:name-test` native.sh_test() wrapping it, which is enough
# for `buck2 test :name-test` to work with zero .buckconfig changes (see
# buck2.md's "buck test" entry for why a plain sh_test() wrapper was
# chosen over writing a bespoke rule - a custom rule would still need this
# same two-target shape under the hood, since a rule can't invoke another
# rule's impl inline, so it would just mean re-implementing sh_test's own
# ExternalRunnerTestInfo wiring ourselves for no functional gain).
# for `buck2 test :name-test` to work with zero .buckconfig changes.
#
# `test_args`/`test_env` cover the one real wrinkle: a test-suite that
# shells out to another buck2-built tool (e.g. glean-clang's clang-index)
# needs that tool's location passed in explicitly via a `$(exe ...)`
# string-parameter macro, rather than relying on it being on `$PATH` -
# more hermetic than this migration's own earlier practice of manually
# prepending PATH by hand to reproduce these runs (see buck2.md).
# prepending PATH by hand to reproduce these runs.
#
# `LANG` defaults to a UTF-8 locale: unlike `buck2 run` (which inherits
# the caller's shell environment, `LANG` included), `buck2 test` runs
Expand Down
5 changes: 4 additions & 1 deletion prelude/decls/haskell_common.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,11 @@ def _deps_arg():

def _compiler_flags_arg():
return {
# attrs.arg(), not attrs.string(): lets a flag carry a
# `$(location ...)`/`$(exe ...)` macro that resolves to a real
# dependency edge.
"compiler_flags": attrs.list(
attrs.string(),
attrs.arg(),
default = [],
doc = """
Flags to pass to the Haskell compiler when compiling this rule's sources.
Expand Down
8 changes: 0 additions & 8 deletions prelude/rules_impl.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -450,14 +450,6 @@ _haskell_extra_attributes = {
},
"haskell_library": {
"preferred_linkage": attrs.enum(Linkage.values(), default = "any"),
# No `dynamic_too` attr here any more (LOCAL FORK - see
# buck2.md): whether to build the static archive with
# `-dynamic-too` (deriving the shared library from that same
# compile's .dyn_o output, instead of compiling "static" and
# "shared" independently) is now driven directly from
# `haskell_toolchain.dynamic_ghc` in haskell.bzl's own
# `haskell_library_impl`, not a per-target attribute - see that
# field's own comment (toolchain.bzl) for why.
"template_deps": attrs.list(attrs.exec_dep(providers = [HaskellLibraryProvider]), default = []),
"_cxx_toolchain": toolchains_common.cxx(),
"_haskell_toolchain": toolchains_common.haskell(),
Expand Down
27 changes: 14 additions & 13 deletions thrift.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -44,19 +44,20 @@ _GEN_PREFIX = "gen-hs2"
# "."; see also Thrift.Compiler.parseThriftFileE: `baseDir </> path`, and
# FilePath.(</>) only ignores baseDir when `path` is already absolute).
#
# A directory named directly on the command line (a plain "-I some/dir"
# string, or a glob'd filegroup covering "everything in this package") is
# a bad fit for that: a plain path can't tell "this cell built standalone"
# apart from "this cell built nested inside another project at a different
# filesystem depth" (see buck2.md's hsthrift-cell entry), and a glob'd
# directory isn't hermetic - remote execution materializes only an
# action's *declared* inputs, not whatever real files happen to sit next
# to the main one in the source tree (which is also what made a shared
# "thrift" symlink inside both lib/ and tests/ - see thrift/BUCK at the
# cell root - actively break: Buck2 saw one input (a glob covering the
# symlink itself) wanting it to be a leaf/symlink, and another input
# (an explicit thrift_file reference reached *through* it) wanting the
# same path to be a real directory, and refused to merge the two).
# A directory named directly on the command line (a plain "-I
# some/dir" string, or a glob'd filegroup covering "everything in this
# package") is a bad fit for that: a plain path can't tell "this cell
# built standalone" apart from "this cell built nested inside another
# project at a different filesystem depth", and a glob'd directory
# isn't hermetic - remote execution materializes only an action's
# *declared* inputs, not whatever real files happen to sit next to the
# main one in the source tree (which is also what made a shared
# "thrift" symlink inside both lib/ and tests/ - see thrift/BUCK at
# the cell root - actively break: Buck2 saw one input (a glob covering
# the symlink itself) wanting it to be a leaf/symlink, and another
# input (an explicit thrift_file reference reached *through* it)
# wanting the same path to be a real directory, and refused to merge
# the two).
#
# So instead: every file an `include` statement might ever need is reached
# through `deps` - other thrift_srcs_export()/thrift_library() targets,
Expand Down
Loading