diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -169,6 +169,21 @@
 targets - take a look at the comments in the generated code to see
 how.
 
+The overrides can also go in a `cabal-buck2.bzl` file in the root of
+the project, for all of its packages at once, which is useful when a
+package's directory isn't yours to add files to (a git submodule, say):
+
+```
+OVERRIDES = {
+    # package name -> component name -> extra attributes
+    "my-package": {
+        "my-package": {"compiler_flags": ["-O2"]},
+    },
+}
+```
+
+Re-run `cabal buck2` after adding or removing `cabal-buck2.bzl`.
+
 You can also completely override the generated targets and write a
 `BUCK` file with your own rules, while still making use of the
 pre-built dependencies that `cabal buck2` produces.  For example, the
@@ -387,6 +402,22 @@
 also applies to dependencies when `--source-deps` is being used.  As
 with other unsupported features, you can use `--keep-going` to just
 skip all the affected packages.
+
+If a package's `Setup.hs` isn't needed by your build, or what it
+does can be done some other way, `--enable-custom-setup` builds it as
+if it had `build-type: Simple` (or `Configure`, if it has a
+`configure` script). The modules that its `Setup.hs` would generate
+(its `autogen-modules`) must then be given by the project, in the
+overrides of the component (see [Customising the build](#customising-the-build)),
+for example
+
+```
+OVERRIDES = {
+    "my-package": {
+        "my-package": {"srcs": {"My.Generated.Module": "//gen:my-module"}},
+    },
+}
+```
 
 ## Hooks build type
 
diff --git a/cabal-buck2.cabal b/cabal-buck2.cabal
--- a/cabal-buck2.cabal
+++ b/cabal-buck2.cabal
@@ -1,6 +1,6 @@
 cabal-version:      3.8
 name:               cabal-buck2
-version:            0.3.0.0
+version:            0.4.0.0
 
 -- To ship a new version:
 --   - bump the version above
@@ -37,6 +37,8 @@
   test/fixtures/**/*.cabal
   test/fixtures/**/*.h
   test/fixtures/**/*.hs
+  test/fixtures/**/*.hs-boot
+  test/fixtures/**/*.pc
   test/fixtures/**/*.project
   test/fixtures/**/.buckconfig
   test/fixtures/**/.gitkeep
@@ -121,6 +123,7 @@
   build-depends:
     , base          >=4.17 && <4.24
     , bytestring    >=0.11.5 && <0.12
+    , Cabal
     , cabal-buck2
     , containers    >=0.6.7 && <0.7
     , directory
diff --git a/changelog.md b/changelog.md
--- a/changelog.md
+++ b/changelog.md
@@ -1,3 +1,24 @@
+# 0.4.0.0
+
+  * For `cabal-install` version 3.18
+  * `--enable-custom-setup` builds the packages with `build-type:
+    Custom` as if they had `build-type: Configure` (if they have a
+    `configure` script) or `Simple`, instead of skipping them. Their
+    `Setup.hs` isn't run, so the modules it would generate (its
+    `autogen-modules`) must be provided by the project.
+  * A project can override the generated targets of its packages in a
+    `cabal-buck2.bzl` at its root, rather than in each package's
+    `BUCK` file.
+  * A package's `extra-source-files` are a target,
+    `<package>-extra-source-files`, for other targets that need them
+    (a generator of the package's sources, say).
+  * The `.hs-boot` file of a module is an input of the module's
+    compilation, and is found next to its source when that is
+    generated (by `happy`, say).
+  * Pass `extra-lib-dirs` and `ld-options` through to Buck2. This
+    fixes some cases of `pkgconfig-depends` not working correctly
+    with Buck2, amongst other things.
+
 # 0.3.0.0
 
   * For `cabal-install` version 3.18
diff --git a/src/Distribution/Client/Buck2/Flags.hs b/src/Distribution/Client/Buck2/Flags.hs
--- a/src/Distribution/Client/Buck2/Flags.hs
+++ b/src/Distribution/Client/Buck2/Flags.hs
@@ -20,10 +20,11 @@
   { buck2CacheAddress :: Flag String
   , buck2NoCache :: Flag Bool
   , buck2SourceDeps :: Flag Bool
+  , buck2CustomSetup :: Flag Bool
   }
 
 defaultBuck2Flags :: Buck2Flags
-defaultBuck2Flags = Buck2Flags{buck2CacheAddress = mempty, buck2NoCache = mempty, buck2SourceDeps = mempty}
+defaultBuck2Flags = Buck2Flags{buck2CacheAddress = mempty, buck2NoCache = mempty, buck2SourceDeps = mempty, buck2CustomSetup = mempty}
 
 buck2FlagOptions :: ShowOrParseArgs -> [OptionField Buck2Flags]
 buck2FlagOptions _ =
@@ -47,6 +48,13 @@
       "Build the dependencies with buck2, from their sources (unpacked under dist-newstyle), like the packages of the project, instead of having cabal build them into its store. Nothing is then taken from the store."
       buck2SourceDeps
       (\v f -> f{buck2SourceDeps = v})
+      trueArg
+  , option
+      []
+      ["enable-custom-setup"]
+      "Build the packages with build-type: Custom as if they had build-type: Configure (if they have a configure script) or Simple, rather than skipping them. Their Setup.hs isn't run: the modules it would generate must be provided (see buck2/cabal.bzl)."
+      buck2CustomSetup
+      (\v f -> f{buck2CustomSetup = v})
       trueArg
   ]
 
diff --git a/src/Distribution/Client/Buck2/Generate.hs b/src/Distribution/Client/Buck2/Generate.hs
--- a/src/Distribution/Client/Buck2/Generate.hs
+++ b/src/Distribution/Client/Buck2/Generate.hs
@@ -27,7 +27,7 @@
 import Distribution.Client.Compat.Prelude
 import Prelude ()
 
-import System.FilePath (isRelative, makeRelative, normalise, (<.>), (</>))
+import System.FilePath (isRelative, makeRelative, normalise, replaceExtension, (<.>), (</>))
 
 import Data.Either (fromLeft)
 import qualified Data.Map as Map
@@ -52,6 +52,7 @@
   , buildToolDepends
   , asmOptions
   , asmSources
+  , autogenModules
   , cSources
   , cmmSources
   , jsSources
@@ -61,8 +62,11 @@
   , cxxSources
   , dataDir
   , dataFiles
+  , extraSrcFiles
   , usedExtensions
   , defaultLanguage
+  , extraLibDirs
+  , ldOptions
   , extraLibs
   , hcOptions
   , hsSourceDirs
@@ -176,6 +180,15 @@
 componentTargets :: SpecComponent -> [AutogenFile] -> ComponentTargets
 componentTargets component files = mempty{ctSpec = [component], ctAutogen = files}
 
+-- | What the modules of a package's components are found in.
+data Sources = Sources
+  { sourceFiles :: Set FilePath
+  -- ^ The package's source files, relative to its directory.
+  , providedModules :: Set ModuleName.ModuleName
+  -- ^ The modules that the package's own @Setup.hs@ would have generated,
+  -- which the project provides instead (see 'SrcProvided').
+  }
+
 -- | Generate the buck2 targets for every buildable component of a local
 -- package, along with the warnings for the components that had to be
 -- skipped (and why). This does no IO: the source files it looks for are
@@ -201,12 +214,26 @@
   -- ^ Whether this is a package of the project, as opposed to a dependency
   -- that is built like one. A dependency has components that nothing
   -- needs (its tests, say), which are skipped without a warning.
+  -> Bool
+  -- ^ Whether the package has @build-type: Custom@, and is built as if it
+  -- didn't (@--enable-custom-setup@), so its @Setup.hs@ isn't run: the
+  -- modules that would have generated (its @autogen-modules@, other than
+  -- the ones generated here) are up to the project.
   -> PackageDescription
   -> (PackageTargets, [String])
-generatePackageTargets localIndex projectRoot componentLBIs externalBuildTools projectTestOptions sources skippedLibs inProject pkgDesc =
+generatePackageTargets localIndex projectRoot componentLBIs externalBuildTools projectTestOptions sources skippedLibs inProject customSetup pkgDesc =
   (targets, concatMap ctWarnings results)
   where
-    generate = generateComponent localIndex projectRoot componentLBIs externalBuildTools projectTestOptions sources skippedLibs inProject pkgDesc
+    generate = generateComponent localIndex projectRoot componentLBIs externalBuildTools projectTestOptions (Sources sources provided) skippedLibs inProject pkgDesc
+    provided
+      | customSetup =
+          Set.fromList
+            [ m
+            | comp <- pkgBuildableComponents pkgDesc
+            , m <- autogenModules (componentBuildInfo comp)
+            , m /= autogenPathsModuleName pkgDesc
+            ]
+      | otherwise = Set.empty
     comps = map configured (pkgBuildableComponents pkgDesc)
 
     -- The component as configuring left it: with what a @configure@ script
@@ -216,7 +243,7 @@
       lookupComponent (localPkgDescr lbi) (componentName comp)
 
     results = map generate comps
-    components = concatMap ctSpec results
+    components = [c{scBootSrcs = bootSources sources (scSrcs c)} | c <- concatMap ctSpec results]
     targets =
       PackageTargets
         { ptSpec = packageSpec pkgDesc (packageGhcOptions pkgDesc componentLBIs) components
@@ -253,7 +280,7 @@
   -> Map (PackageName, ComponentName) LocalBuildInfo
   -> Set String
   -> Map (PackageName, ComponentName) [PathTemplate]
-  -> Set FilePath
+  -> Sources
   -> Set (PackageName, LibraryName)
   -> Bool
   -> PackageDescription
@@ -294,7 +321,7 @@
     ifNotOnSkippedLib bi targetName kind act
       | isNothing (lbiClbiFor pkgDesc componentLBIs comp) = notConfigured (kindName kind ++ " " ++ targetName)
       | buildType pkgDesc == Custom =
-          skip (kindName kind ++ " " ++ targetName ++ " (build-type: Custom needs its Setup.hs to be run)")
+          skip (kindName kind ++ " " ++ targetName ++ " (build-type: Custom needs its Setup.hs to be run, unless --enable-custom-setup)")
       | unsupported : _ <- unsupportedSources bi =
           skip (kindName kind ++ " " ++ targetName ++ " (" ++ unsupported ++ " aren't supported)")
       | otherwise =
@@ -345,7 +372,7 @@
     -- test-suite and benchmark: a @main-is@ file over the @other-modules@.
     mainIsComponent kind targetName bi mainIs extra = case lbiClbiFor pkgDesc componentLBIs comp of
       Nothing -> notConfigured (kindName kind ++ " " ++ targetName)
-      Just (lbi, clbi) -> case (resolveMainIs sources bi mainIs, resolveModules sources pkgDesc (Just (lbi, clbi)) bi (otherModules bi)) of
+      Just (lbi, clbi) -> case (resolveMainIs (sourceFiles sources) bi mainIs, resolveModules sources pkgDesc (Just (lbi, clbi)) bi (otherModules bi)) of
         (Right mainSrc, Right (otherSrcs, srcAutogen)) ->
           executableLike kind targetName lbi clbi bi (SrcFile mainSrc) otherSrcs (extra lbi) srcAutogen
         (mainRes, othersRes) ->
@@ -466,6 +493,7 @@
     , specGhcOptions = projectGhcOptions
     , specDataDir = getSymbolicPath (dataDir pkgDesc)
     , specDataFiles = map getSymbolicPath (dataFiles pkgDesc)
+    , specExtraSourceFiles = map getSymbolicPath (extraSrcFiles pkgDesc)
     , specComponents = components
     }
 
@@ -479,6 +507,7 @@
     , scExeName = Nothing
     , scMainIs = Nothing
     , scSrcs = []
+    , scBootSrcs = []
     , scTestArgs = []
     , scGhcOptions = hcOptions GHC bi
     , scCppOptions = cppOptions bi
@@ -486,6 +515,8 @@
       scLanguage = Just (prettyShow (fromMaybe Haskell98 (defaultLanguage bi)))
     , scExtensions = map prettyShow (usedExtensions bi)
     , scExtraLibraries = extraLibs bi
+    , scLdOptions = ldOptions bi
+    , scExtraLibDirs = map getSymbolicPath (extraLibDirs bi)
     , scDeps = map specDep (libraryDeps bi)
     , scReexports = []
     , scBuildTools = mapMaybe buildToolSpec (ordNub [(pn, exe) | ExeDependency pn exe _ <- buildToolDepends bi])
@@ -689,13 +720,13 @@
 -- while evaluating the @BUCK@ file, which (since a @buck2 build //...@
 -- evaluates every @BUCK@ file up front) would otherwise take the *entire*
 -- build down over one unresolvable module in one component of one package.
-resolveModules :: Set FilePath -> PackageDescription -> Maybe (LocalBuildInfo, ComponentLocalBuildInfo) -> BuildInfo -> [ModuleName.ModuleName] -> Either [String] ([(String, Src)], [AutogenFile])
+resolveModules :: Sources -> PackageDescription -> Maybe (LocalBuildInfo, ComponentLocalBuildInfo) -> BuildInfo -> [ModuleName.ModuleName] -> Either [String] ([(String, Src)], [AutogenFile])
 resolveModules sources pkgDesc mlbiClbi bi mods =
   case partitionEithers (map (resolveOne sources pkgDesc mlbiClbi bi) mods) of
     ([], triples) -> Right ([(n, v) | (n, v, _) <- triples], concat [fs | (_, _, fs) <- triples])
     (problems, _) -> Left problems
 
-resolveOne :: Set FilePath -> PackageDescription -> Maybe (LocalBuildInfo, ComponentLocalBuildInfo) -> BuildInfo -> ModuleName.ModuleName -> Either String (String, Src, [AutogenFile])
+resolveOne :: Sources -> PackageDescription -> Maybe (LocalBuildInfo, ComponentLocalBuildInfo) -> BuildInfo -> ModuleName.ModuleName -> Either String (String, Src, [AutogenFile])
 resolveOne sources pkgDesc mlbiClbi bi m
   | m == autogenPathsModuleName pkgDesc = case mlbiClbi of
       Nothing ->
@@ -703,14 +734,16 @@
       Just (lbi, clbi) ->
         let file = pathsModuleFile pkgDesc lbi clbi m
          in Right (prettyShow m, SrcAutogen (autogenName file), [file])
-  | otherwise = case firstExisting sources (moduleCandidates bi m) of
+  | otherwise = case firstExisting (sourceFiles sources) (moduleCandidates bi m) of
       Just real -> Right (prettyShow m, SrcFile real, [])
-      Nothing ->
-        Left $
-          "cabal buck2: couldn't find a source file for module "
-            ++ prettyShow m
-            ++ " under "
-            ++ intercalate ", " (sourceDirs bi)
+      Nothing
+        | m `Set.member` providedModules sources -> Right (prettyShow m, SrcProvided, [])
+        | otherwise ->
+            Left $
+              "cabal buck2: couldn't find a source file for module "
+                ++ prettyShow m
+                ++ " under "
+                ++ intercalate ", " (sourceDirs bi)
 
 -- | Cabal's own Setup.hs generates a @Paths_\<pkg\>@ module fresh at
 -- configure\/build time (giving @version@\/@getDataFileName@\/etc) - no
@@ -776,6 +809,15 @@
 moduleCandidates :: BuildInfo -> ModuleName.ModuleName -> [FilePath]
 moduleCandidates bi m = [normalise (dir </> ModuleName.toFilePath m <.> ext) | dir <- sourceDirs bi, ext <- ["hs", "lhs", "hsc", "x", "y", "ly"]]
 
+-- | Where GHC looks for the boot file of a module whose source is the given
+-- file: next to it.
+bootCandidates :: FilePath -> [FilePath]
+bootCandidates src = [replaceExtension src ext | ext <- ["hs-boot", "lhs-boot"]]
+
+-- | The boot files of the modules with the given sources, that exist.
+bootSources :: Set FilePath -> [(String, Src)] -> [(String, FilePath)]
+bootSources sources srcs = [(m, boot) | (m, SrcFile src) <- srcs, Just boot <- [firstExisting sources (bootCandidates src)]]
+
 mainIsCandidates :: BuildInfo -> FilePath -> [FilePath]
 mainIsCandidates bi mainIs = [normalise (dir </> mainIs) | dir <- sourceDirs bi]
 
@@ -810,7 +852,7 @@
       CFLib _ -> []
       where
         bi = componentBuildInfo comp
-    modules bi = concatMap (moduleCandidates bi)
+    modules bi = concatMap (\m -> let cs = moduleCandidates bi m in cs ++ concatMap bootCandidates cs)
     mainIs = mainIsCandidates
 
 sourceDirs :: BuildInfo -> [FilePath]
diff --git a/src/Distribution/Client/Buck2/LocalPackages.hs b/src/Distribution/Client/Buck2/LocalPackages.hs
--- a/src/Distribution/Client/Buck2/LocalPackages.hs
+++ b/src/Distribution/Client/Buck2/LocalPackages.hs
@@ -12,6 +12,7 @@
   , componentNamesFor
   , BuiltPackage (..)
   , builtLocalPackages
+  , translateCustomSetups
   , rootRelativeDir
   , nonLibraryTargetName
   , ToolTarget (..)
@@ -31,6 +32,7 @@
 
 import Distribution.Client.DistDirLayout (DistDirLayout (distUnpackedSrcDirectory, distUnpackedSrcRootDirectory))
 import qualified Distribution.Client.InstallPlan as InstallPlan
+import qualified Distribution.Compat.Graph as Graph
 import Distribution.Client.ProjectPlanning
   ( ElaboratedConfiguredPackage (..)
   , ElaboratedInstallPlan
@@ -47,11 +49,11 @@
 import Distribution.Client.Types.PackageLocation (PackageLocation (..))
 import Distribution.Types.UnitId (UnitId, newSimpleUnitId)
 
-import Distribution.Package (HasUnitId (installedUnitId), PackageInstalled (installedDepends), PackageName, packageId, packageName, unPackageName)
+import Distribution.Package (HasUnitId (installedUnitId), PackageId, PackageInstalled (installedDepends), PackageName, packageId, packageName, unPackageName)
 import Distribution.PackageDescription (PackageDescription)
 import qualified Distribution.PackageDescription as PD
 import Distribution.Simple.InstallDirs (PathTemplate)
-import Distribution.Simple.Utils (die', info, ordNub)
+import Distribution.Simple.Utils (die', info, ordNub, warn)
 import Distribution.Types.Component (componentBuildInfo, componentName)
 import Distribution.Types.ComponentName (ComponentName (CTestName))
 import Distribution.Types.Dependency (depPkgName)
@@ -204,19 +206,59 @@
   , bpInProject :: Bool
   -- ^ A package of the project, as opposed to a dependency that is built
   -- like one.
+  , bpCustomSetup :: Bool
+  -- ^ A package with @build-type: Custom@ that is built as if it didn't
+  -- (see 'translateCustomSetups'): 'bpDescription' has the build type it is
+  -- built as.
   }
 
+-- | For @--enable-custom-setup@: the plan, with each package that buck2
+-- builds and that has @build-type: Custom@ built as if it had @build-type:
+-- Configure@ if it has a @configure@ script, or @Simple@ if not (as Hadrian
+-- does), and the packages that are. Their @Setup.hs@ isn't run, so whatever
+-- it does is up to the project.
+translateCustomSetups :: Verbosity -> DependencyMode -> DistDirLayout -> ElaboratedInstallPlan -> IO (ElaboratedInstallPlan, Set PackageId)
+translateCustomSetups verbosity mode distDirLayout plan = do
+  pkgs <- traverse translate (InstallPlan.toList plan)
+  let translated = Map.fromList (mapMaybe snd pkgs)
+  for_ (Map.toList translated) $ \(pkgId, buildType) ->
+    warn verbosity $
+      "cabal buck2: building "
+        ++ prettyShow pkgId
+        ++ " as if it had build-type: "
+        ++ prettyShow buildType
+        ++ " (--enable-custom-setup), so its Setup.hs isn't run"
+  return (InstallPlan.new (InstallPlan.planIndepGoals plan) (Graph.fromDistinctList (map fst pkgs)), Map.keysSet translated)
+  where
+    translate pkg = case pkg of
+      InstallPlan.Configured elab
+        | isBuiltLocally mode elab
+        , PD.buildType desc == PD.Custom -> do
+            dir <- packageSourceDir verbosity distDirLayout elab
+            hasConfigure <- doesFileExist (dir </> "configure")
+            let buildType = if hasConfigure then PD.Configure else PD.Simple
+            return (InstallPlan.Configured elab{elabPkgDescription = desc{PD.buildTypeRaw = Just buildType}}, Just (packageId elab, buildType))
+        where
+          desc = elabPkgDescription elab
+      _ -> return (pkg, Nothing)
+
 -- | Every package buck2 builds from source. Per-component elaboration gives
 -- each such package one 'ElaboratedConfiguredPackage' per component, all
 -- sharing the same directory and 'PackageDescription', so there is one
 -- entry per directory here.
-builtLocalPackages :: Verbosity -> DependencyMode -> DistDirLayout -> ElaboratedInstallPlan -> IO [BuiltPackage]
-builtLocalPackages verbosity mode distDirLayout plan = do
+builtLocalPackages :: Verbosity -> DependencyMode -> DistDirLayout -> Set PackageId -> ElaboratedInstallPlan -> IO [BuiltPackage]
+builtLocalPackages verbosity mode distDirLayout customSetups plan = do
   pkgs <-
     fmap (nubBy ((==) `on` bpDir)) . sequenceA $
       [ do
         dir <- packageSourceDir verbosity distDirLayout elab
-        return BuiltPackage{bpDir = dir, bpDescription = elabPkgDescription elab, bpInProject = elabLocalToProject elab}
+        return
+          BuiltPackage
+            { bpDir = dir
+            , bpDescription = elabPkgDescription elab
+            , bpInProject = elabLocalToProject elab
+            , bpCustomSetup = packageId elab `Set.member` customSetups
+            }
       | elab <- builtLocalElabs mode plan
       ]
   -- Everything that refers to a package does so by its name (a dependency in
diff --git a/src/Distribution/Client/Buck2/Prebuilt.hs b/src/Distribution/Client/Buck2/Prebuilt.hs
--- a/src/Distribution/Client/Buck2/Prebuilt.hs
+++ b/src/Distribution/Client/Buck2/Prebuilt.hs
@@ -23,6 +23,7 @@
   ( generatePrebuilt
   , RepoPaths (..)
   , externalDirs
+  , prebuiltLinkerFlags
   , toRepoRelative
   ) where
 
@@ -506,8 +507,22 @@
     profiled = rel (rpProfiledLibs p)
     globalDbRel = ("ghc-" ++ rpGhcVersion paths) </> "package.conf.d"
     headerDirs = mapMaybe (toRepoRelative paths) (includeDirs info)
-    extraLinkerFlags = ["-l" ++ lib | lib <- extraLibraries info]
+    extraLinkerFlags = prebuiltLinkerFlags info
     depTargets = ordNub [":" ++ t | d <- depends info, Just t <- [Map.lookup d uidToTarget]]
+
+-- | The linker flags a package's @.conf@ file asks for: its @extra-libraries@,
+-- and where to find them. The @library-dirs@ of a package that needs a
+-- system library are also where that library is, when it isn't on the
+-- linker's default path: Cabal records the directory it was found in at
+-- configure time (@extra-lib-dirs@, or a Nix store path), and GHC passes
+-- them all as @-L@ when it links against the package. Without them the
+-- @-l@ flag alone can't be satisfied.
+prebuiltLinkerFlags :: InstalledPackageInfo -> [String]
+prebuiltLinkerFlags info =
+  ["-L" ++ dir | not (null libs), dir <- ordNub (libraryDirs info)]
+    ++ ["-l" ++ lib | lib <- libs]
+  where
+    libs = extraLibraries info
 
 -- | Convert an absolute path under GHC's libdir, the cabal store or one of the
 -- 'externalDirs' into one relative to @third-party\/haskell@, via whichever
diff --git a/src/Distribution/Client/Buck2/Setup.hs b/src/Distribution/Client/Buck2/Setup.hs
--- a/src/Distribution/Client/Buck2/Setup.hs
+++ b/src/Distribution/Client/Buck2/Setup.hs
@@ -66,10 +66,10 @@
 -- | Copy @.buckconfig@ and @PACKAGE@ if they don't exist
 ensureBuckconfigAndPackage :: Verbosity -> FilePath -> IO ()
 ensureBuckconfigAndPackage verbosity projectRoot = do
-  copyIfMissing verbosity (exampleDir </> ".buckconfig") (projectRoot </> ".buckconfig")
-  copyIfMissing verbosity (exampleDir </> "PACKAGE") (projectRoot </> "PACKAGE")
+  copyIfMissing verbosity (templateDir </> ".buckconfig") (projectRoot </> ".buckconfig")
+  copyIfMissing verbosity (templateDir </> "PACKAGE") (projectRoot </> "PACKAGE")
   where
-    exampleDir = projectRoot </> "buck2" </> "example"
+    templateDir = projectRoot </> "buck2" </> "template"
 
 copyIfMissing :: Verbosity -> FilePath -> FilePath -> IO ()
 copyIfMissing verbosity src dest = do
diff --git a/src/Distribution/Client/Buck2/Spec.hs b/src/Distribution/Client/Buck2/Spec.hs
--- a/src/Distribution/Client/Buck2/Spec.hs
+++ b/src/Distribution/Client/Buck2/Spec.hs
@@ -27,6 +27,10 @@
   -- ^ The package's @data-dir@, relative to its directory.
   , specDataFiles :: [FilePath]
   -- ^ Its @data-files@, as patterns, relative to 'specDataDir'.
+  , specExtraSourceFiles :: [FilePath]
+  -- ^ Its @extra-source-files@, as patterns, relative to its directory:
+  -- what its build needs, other than its sources, which something
+  -- elsewhere in the project may need too (a generator, say).
   , specComponents :: [SpecComponent]
   }
 
@@ -53,6 +57,8 @@
   -- ^ The main module's source; not for a library.
   , scSrcs :: [(String, Src)]
   -- ^ Every other module (by module name) and its source.
+  , scBootSrcs :: [(String, FilePath)]
+  -- ^ The @.hs-boot@ file of each module that has one, next to its source.
   , scTestArgs :: [String]
   -- ^ The project's @test-options@ for a test-suite, with template variables
   -- expanded.
@@ -61,6 +67,13 @@
   , scLanguage :: Maybe String
   , scExtensions :: [String]
   , scExtraLibraries :: [String]
+  , scLdOptions :: [String]
+  -- ^ The @ld-options@, as configured: so including the other linker flags of
+  -- the @pkgconfig-depends@, such as an rpath.
+  , scExtraLibDirs :: [FilePath]
+  -- ^ Where to look for the 'scExtraLibraries': the component's
+  -- @extra-lib-dirs@, as configured, so including the directories of its
+  -- @pkgconfig-depends@ (Cabal adds those at configure time).
   , scDeps :: [SpecDep]
   , scReexports :: [SpecReexport]
   -- ^ Modules a library re-exports from the library of another package (or
@@ -87,8 +100,10 @@
 -- | A source file of a component: either a real file in the package
 -- (relative to its directory), or one generated into the package's
 -- @cabal-buck2\/autogen@ directory, named by its @export_file()@ entry there
--- (see 'Distribution.Client.Buck2.Generate.AutogenFile').
-data Src = SrcFile FilePath | SrcAutogen String
+-- (see 'Distribution.Client.Buck2.Generate.AutogenFile'), or a module that
+-- the package's @Setup.hs@ would have generated, which the project must
+-- provide (@--enable-custom-setup@).
+data Src = SrcFile FilePath | SrcAutogen String | SrcProvided
 
 -- | A library a component depends on.
 data SpecDep = SpecDep
diff --git a/src/Distribution/Client/Buck2/Write.hs b/src/Distribution/Client/Buck2/Write.hs
--- a/src/Distribution/Client/Buck2/Write.hs
+++ b/src/Distribution/Client/Buck2/Write.hs
@@ -75,7 +75,7 @@
   traverse_ (writeOnePackage verbosity projectRoot) results
   where
     generate skipped pkg srcs =
-      generatePackageTargets localIndex projectRoot componentLBIs externalBuildTools projectTestOptions srcs skipped (bpInProject pkg) (bpDescription pkg)
+      generatePackageTargets localIndex projectRoot componentLBIs externalBuildTools projectTestOptions srcs skipped (bpInProject pkg) (bpCustomSetup pkg) (bpDescription pkg)
 
     librariesWithoutRule pkg (targets, _) =
       [ (packageName desc, libName lib)
@@ -98,22 +98,32 @@
 -- loads it to find the targets of the packages that a package depends on, and
 -- the paths of its own directory. Nothing in a package's @BUCK.cabal.bzl@ is
 -- relative to the project, so only this file changes when a package moves.
+--
+-- It also passes on the project's own customisation of the generated
+-- targets: the @OVERRIDES@ of a @cabal-buck2.bzl@ in the project root, if
+-- there is one (see @buck2\/cabal.bzl@).
 writePackagesFile :: Verbosity -> FilePath -> [BuiltPackage] -> IO ()
 writePackagesFile verbosity projectRoot pkgs = do
   createDirectoryIfMissing True (takeDirectory path)
-  writeFile path contents
+  hasOverrides <- doesFileExist (projectRoot </> "cabal-buck2.bzl")
+  writeFile path (contents hasOverrides)
   notice verbosity $ "cabal buck2: generated cabal-buck2" </> "packages.bzl" ++ " (" ++ show (length pkgs) ++ " package(s))"
   where
     path = projectRoot </> "cabal-buck2" </> "packages.bzl"
-    contents =
+    contents hasOverrides =
       unlines
-        [ "# @generated by `cabal buck2` - do not edit by hand."
-        , "# Re-run `cabal buck2` after adding, removing or moving a package."
-        , "#"
-        , "# The directory of each package that this project builds, from the root of"
-        , "# the project. See buck2/cabal.bzl."
-        , ""
-        ]
+        ( [ "# @generated by `cabal buck2` - do not edit by hand."
+          , "# Re-run `cabal buck2` after adding, removing or moving a package, or"
+          , "# adding or removing cabal-buck2.bzl."
+          , "#"
+          , "# The directory of each package that this project builds, from the root of"
+          , "# the project, and the project's overrides of the generated targets. See"
+          , "# buck2/cabal.bzl."
+          , ""
+          ]
+            ++ ["load(\"//:cabal-buck2.bzl\", _OVERRIDES = \"OVERRIDES\")" | hasOverrides]
+            ++ ["", "OVERRIDES = " ++ (if hasOverrides then "_OVERRIDES" else "{}"), ""]
+        )
         ++ renderBinding "PACKAGES" (VDict (Map.toList packages))
     packages =
       Map.fromList
@@ -204,6 +214,7 @@
       ++ [ ("data", VDict [("dir", str (specDataDir spec)), ("files", strList (specDataFiles spec))])
          | not (null (specDataFiles spec))
          ]
+      ++ listField "extra_source_files" (specExtraSourceFiles spec)
       ++ [("components", VList (map componentValue (specComponents spec)))]
 
 componentValue :: SpecComponent -> Value
@@ -213,12 +224,15 @@
       ++ [("exe_name", str exe) | Just exe <- [scExeName c]]
       ++ [("main_is", srcValue src) | Just src <- [scMainIs c]]
       ++ [("srcs", VDict [(m, srcValue src) | (m, src) <- scSrcs c]) | not (null (scSrcs c))]
+      ++ [("boot_srcs", VDict [(m, str boot) | (m, boot) <- scBootSrcs c]) | not (null (scBootSrcs c))]
       ++ listField "test_args" (scTestArgs c)
       ++ listField "ghc_options" (scGhcOptions c)
       ++ listField "cpp_options" (scCppOptions c)
       ++ [("language", str lang) | Just lang <- [scLanguage c]]
       ++ listField "extensions" (scExtensions c)
       ++ listField "extra_libraries" (scExtraLibraries c)
+      ++ listField "extra_lib_dirs" (scExtraLibDirs c)
+      ++ listField "ld_options" (scLdOptions c)
       ++ valuesField "deps" (map depValue (scDeps c))
       ++ valuesField "reexports" (map reexportValue (scReexports c))
       ++ valuesField "build_tools" (map buildToolValue (scBuildTools c))
@@ -237,6 +251,7 @@
 srcValue :: Src -> Value
 srcValue (SrcFile path) = str path
 srcValue (SrcAutogen name) = VDict [("autogen", str name)]
+srcValue SrcProvided = VDict [("provided", VBool True)]
 
 depValue :: SpecDep -> Value
 depValue d =
diff --git a/src/Distribution/Client/CmdBuck2.hs b/src/Distribution/Client/CmdBuck2.hs
--- a/src/Distribution/Client/CmdBuck2.hs
+++ b/src/Distribution/Client/CmdBuck2.hs
@@ -36,6 +36,8 @@
 import Distribution.Client.Compat.Prelude
 import Prelude ()
 
+import qualified Data.Set as Set
+
 import Distribution.Client.DistDirLayout (DistDirLayout (distProjectRootDirectory))
 import Distribution.Client.NixStyleOptions
   ( NixStyleFlags (..)
@@ -70,9 +72,10 @@
 import Distribution.Client.Buck2.BuildDependencies (buildDependencies)
 import Distribution.Client.Buck2.Cache (cacheSetting, configureCache)
 import Distribution.Client.Buck2.Configure (configureComponents)
-import Distribution.Client.Buck2.Flags (Buck2Flags, buck2FlagOptions, defaultBuck2Flags, dependencyMode)
+import Distribution.Client.Buck2.Flags (Buck2Flags (buck2CustomSetup), buck2FlagOptions, defaultBuck2Flags, dependencyMode)
 import Distribution.Client.Buck2.LocalPackages
   ( builtLocalPackages
+  , translateCustomSetups
   , localBuildTools
   , prebuiltUnits
   , localToolTargets
@@ -144,12 +147,17 @@
       let projectRoot = distProjectRootDirectory (distDirLayout baseCtx)
       checkBuck2Prelude verbosity projectRoot
 
-      buildCtx <- buildDependencies verbosity mode baseCtx targetSelectors
+      builtCtx <- buildDependencies verbosity mode baseCtx targetSelectors
+      (plan, customSetups) <-
+        if fromFlagOrDefault False (buck2CustomSetup (extraFlags flags))
+          then translateCustomSetups verbosity mode (distDirLayout baseCtx) (elaboratedPlanOriginal builtCtx)
+          else return (elaboratedPlanOriginal builtCtx, Set.empty)
+      let buildCtx = builtCtx{elaboratedPlanOriginal = plan}
 
       ensureBuckconfigAndPackage verbosity projectRoot
       configureCache verbosity projectRoot cache
 
-      localPkgs <- builtLocalPackages verbosity mode (distDirLayout baseCtx) (elaboratedPlanOriginal buildCtx)
+      localPkgs <- builtLocalPackages verbosity mode (distDirLayout baseCtx) customSetups (elaboratedPlanOriginal buildCtx)
 
       (externalBuildTools, resolvedDeps) <-
         generatePrebuilt
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -29,8 +29,9 @@
 
 import Distribution.Client.Buck2.Cache (cacheBlock, spliceBlock)
 import Distribution.Client.Buck2.Fingerprint (ccFingerprintOf, fingerprintOf)
-import Distribution.Client.Buck2.Prebuilt (RepoPaths (..), externalDirs, toRepoRelative)
+import Distribution.Client.Buck2.Prebuilt (RepoPaths (..), externalDirs, prebuiltLinkerFlags, toRepoRelative)
 import Distribution.Client.Buck2.Version (cabalBuck2Version)
+import Distribution.InstalledPackageInfo (InstalledPackageInfo (..), emptyInstalledPackageInfo)
 
 main :: IO ()
 main = do
@@ -52,7 +53,7 @@
     exitFailure
 
 unitTests :: [(String, IO ())]
-unitTests = [("fingerprint", fingerprint), ("cc-fingerprint", ccFingerprint), ("splice-block", spliceBlockTest), ("external-dirs", externalDirsTest)]
+unitTests = [("fingerprint", fingerprint), ("cc-fingerprint", ccFingerprint), ("splice-block", spliceBlockTest), ("external-dirs", externalDirsTest), ("linker-flags", linkerFlagsTest)]
 
 runUnitTest :: (String, IO ()) -> IO Bool
 runUnitTest (name, test) = do
@@ -73,8 +74,31 @@
   , ("custom-setup", customSetup)
   , ("reexports", reexports)
   , ("buck2-version", buck2Version)
+  , ("project-overrides", projectOverrides)
+  , ("pkgconfig", pkgconfig)
+  , ("project-overrides", projectOverrides)
+  , ("same-name", sameName)
   ]
 
+-- | A library and an executable with the same name in one package, and
+-- another package that needs that executable as a build tool.
+sameName :: Project -> IO ()
+sameName project = do
+  _ <- buck2 project []
+
+  -- The executable can't have the library's target name, so it gets a
+  -- suffix - and a build-tool-depends on it from another package has to
+  -- name it the same way, or buck2 won't find the target.
+  toolBzl <- readIn project ("tool" </> "BUCK.cabal.bzl")
+  let tool = assertContains "tool/BUCK.cabal.bzl"
+  tool "'kind': 'library'" toolBzl
+  tool "'name': 'tool'" toolBzl
+  tool "'kind': 'executable'" toolBzl
+  tool "'name': 'tool-exe'" toolBzl
+  userBzl <- readIn project ("user" </> "BUCK.cabal.bzl")
+  assertContains "user/BUCK.cabal.bzl" "'exe': 'tool-exe'" userBzl
+  assertContains "user/BUCK.cabal.bzl" "'package': 'tool'" userBzl
+
 -- | A copy of a fixture project, and how to run @cabal-buck2@ in it.
 data Project = Project
   { projectDir :: FilePath
@@ -100,6 +124,7 @@
       "disabled-stanzas" -> "basic"
       "cache-config" -> "basic"
       "buck2-version" -> "basic"
+      "project-overrides" -> "basic"
       _ -> name
     setup dir = do
       cleanup dir
@@ -114,9 +139,11 @@
       -- config (which $CABAL_DIR and $CABAL_CONFIG may point to, as in
       -- haskell-ci) are neither used nor changed.
       let cabalDir = dir </> "cabal-dir"
+          -- A fixture can have pkg-config files of its own, in pkgconfig/.
+          pkgConfigPath = dir </> "project" </> "pkgconfig" ++ maybe "" (':' :) (lookup "PKG_CONFIG_PATH" env)
           cabalEnv =
-            [("CABAL_DIR", cabalDir), ("CABAL_CONFIG", cabalDir </> "config")]
-              ++ [kv | kv@(k, _) <- env, k `notElem` ["CABAL_DIR", "CABAL_CONFIG"]]
+            [("CABAL_DIR", cabalDir), ("CABAL_CONFIG", cabalDir </> "config"), ("PKG_CONFIG_PATH", pkgConfigPath)]
+              ++ [kv | kv@(k, _) <- env, k `notElem` ["CABAL_DIR", "CABAL_CONFIG", "PKG_CONFIG_PATH"]]
           run args = do
             (code, out, err) <-
               readCreateProcessWithExitCode
@@ -270,6 +297,14 @@
   lib "local_build_spec" libBzl
   lib "'kind': 'library'" libBzl
   lib "'name': 'lib-pkg'" libBzl
+  -- extra-source-files reach the spec as they are (patterns), for
+  -- cabal.bzl to glob.
+  lib "'extra_source_files': [\n        'include/*.h',\n    ]" libBzl
+  -- extra-lib-dirs, as configured (so with the directories of any
+  -- pkgconfig-depends), go in the spec for the link.
+  lib "'extra_lib_dirs': [\n                'libs',\n            ]" libBzl
+  -- A module's boot file is found next to its source.
+  lib "'boot_srcs': {\n                'MyLib': 'src/MyLib.hs-boot',\n            }" libBzl
   noLib "haskell_library(" libBzl
   -- Project-level options and test options are only recorded where they
   -- apply.
@@ -447,6 +482,25 @@
   assertContains "user/BUCK.cabal.bzl" "'package': 're-lib'" userBzl
   assertNotContains "user/BUCK.cabal.bzl" "base-lib" userBzl
 
+-- | What Cabal's configure makes of @pkgconfig-depends@ and @extra-lib-dirs@ is
+-- in the spec as it is, for the link: also for a component that has no C
+-- sources, which buck2 doesn't run pkg-config for. The fixture has a
+-- @foo.pc@ with its own library and include directories, a define and an
+-- rpath.
+pkgconfig :: Project -> IO ()
+pkgconfig project = do
+  _ <- buck2 project []
+  bzl <- readIn project ("lib" </> "BUCK.cabal.bzl")
+  let spec = assertContains "lib/BUCK.cabal.bzl"
+  spec "'extra_libraries': [\n                'foo',\n            ]" bzl
+  -- The directory of the package's own extra-lib-dirs and then that of pkg-config.
+  spec "'extra_lib_dirs': [\n                'plain',\n                '/opt/foo/lib',\n            ]" bzl
+  spec "'ld_options': [\n                '-Wl,-rpath,/opt/foo/lib',\n            ]" bzl
+  spec "'include_dirs': [\n                '/opt/foo/include',\n            ]" bzl
+  spec "'cc_options': [\n                '-DFOO_ENABLED',\n            ]" bzl
+  -- and for buck2 to run pkg-config for the C sources there are.
+  spec "'pkgconfig': [\n                'foo',\n            ]" bzl
+
 -- | The checkout of haskell-buck2 has to be for this release of cabal-buck2:
 -- if it is not, or doesn't say, the error says which branch is needed.
 buck2Version :: Project -> IO ()
@@ -486,6 +540,33 @@
   depBzl <- readIn project ("dist-newstyle" </> "src" </> "custom-dep-0.1" </> "BUCK.cabal.bzl")
   assertNotContains "custom-dep BUCK.cabal.bzl" "'kind': 'library'" depBzl
 
+  -- With --enable-custom-setup it is built as if it had build-type: Simple,
+  -- and the module its Setup.hs would generate is left to the project.
+  translated <- buck2 project ["--source-deps", "--enable-custom-setup"]
+  assertContains "warning" "custom-dep-0.1 as if it had build-type: Simple" translated
+  assertNotContains "output" "skipping" translated
+  translatedBzl <- readIn project ("dist-newstyle" </> "src" </> "custom-dep-0.1" </> "BUCK.cabal.bzl")
+  assertContains "custom-dep BUCK.cabal.bzl" "'kind': 'library'" translatedBzl
+  assertContains "custom-dep BUCK.cabal.bzl" "'Custom.Generated': {\n                    'provided': True," translatedBzl
+  appBzl <- readIn project ("app" </> "BUCK.cabal.bzl")
+  assertContains "app BUCK.cabal.bzl" "'kind': 'executable'" appBzl
+
+-- | A project's @cabal-buck2.bzl@, with its overrides of the generated
+-- targets, is loaded by @packages.bzl@, and an empty set of overrides is
+-- given there if there is none.
+projectOverrides :: Project -> IO ()
+projectOverrides project = do
+  _ <- buck2 project []
+  without <- readIn project ("cabal-buck2" </> "packages.bzl")
+  assertContains "packages.bzl" "OVERRIDES = {}" without
+  assertNotContains "packages.bzl" "cabal-buck2.bzl\"" without
+
+  writeFile (projectDir project </> "cabal-buck2.bzl") "OVERRIDES = {}\n"
+  _ <- buck2 project []
+  with <- readIn project ("cabal-buck2" </> "packages.bzl")
+  assertContains "packages.bzl" "load(\"//:cabal-buck2.bzl\", _OVERRIDES = \"OVERRIDES\")" with
+  assertContains "packages.bzl" "OVERRIDES = _OVERRIDES" with
+
 -- | @--cache=ADDRESS@ adds the cache's settings to @.buckconfig@, which are
 -- kept by later runs without the flag, and @--no-cache@ removes.
 cacheConfig :: Project -> IO ()
@@ -597,3 +678,23 @@
   check "a library under GHC's libdir" (Just "ghc-9.6.7/x86_64-linux-ghc-9.6.7/base-4.18.3.0/libHSbase.a") (toRepoRelative paths' "/ghc/lib/x86_64-linux-ghc-9.6.7/base-4.18.3.0/libHSbase.a")
   check "a library in the store" (Just "cabal-store/foo-1/lib/libHSfoo.a") (toRepoRelative paths' "/home/u/.cabal/store/ghc-9.6.7/foo-1/lib/libHSfoo.a")
   check "something that isn't linked" Nothing (toRepoRelative paths' "/usr/include/stdio.h")
+
+-- | The @-L@ flags for a prebuilt package's @extra-libraries@ come from its
+-- @library-dirs@, as they do when GHC links against the package itself; a
+-- package without any needs no @-L@ flags at all.
+linkerFlagsTest :: IO ()
+linkerFlagsTest = do
+  let withLibs =
+        emptyInstalledPackageInfo
+          { libraryDirs = ["/nix/store/aaa-hmatrix-0.20.2/lib/x86_64-linux-ghc-9.6.7/hmatrix-0.20.2-X", "/nix/store/bbb-openblas-0.3.33/lib", "/nix/store/bbb-openblas-0.3.33/lib"]
+          , extraLibraries = ["openblas"]
+          }
+      withoutLibs = emptyInstalledPackageInfo{libraryDirs = ["/nix/store/ccc-text-2.1.4/lib"]}
+      check what expected actual =
+        unless (expected == actual) $
+          failure (what ++ ": expected " ++ show expected ++ ", got " ++ show actual)
+  check
+    "a package with extra-libraries"
+    ["-L/nix/store/aaa-hmatrix-0.20.2/lib/x86_64-linux-ghc-9.6.7/hmatrix-0.20.2-X", "-L/nix/store/bbb-openblas-0.3.33/lib", "-lopenblas"]
+    (prebuiltLinkerFlags withLibs)
+  check "a package without" [] (prebuiltLinkerFlags withoutLibs)
diff --git a/test/fixtures/basic/lib-pkg/include/lib-pkg.h b/test/fixtures/basic/lib-pkg/include/lib-pkg.h
new file mode 100644
--- /dev/null
+++ b/test/fixtures/basic/lib-pkg/include/lib-pkg.h
@@ -0,0 +1,1 @@
+/* For the test of extra-source-files. */
diff --git a/test/fixtures/basic/lib-pkg/lib-pkg.cabal b/test/fixtures/basic/lib-pkg/lib-pkg.cabal
--- a/test/fixtures/basic/lib-pkg/lib-pkg.cabal
+++ b/test/fixtures/basic/lib-pkg/lib-pkg.cabal
@@ -3,8 +3,13 @@
 version:             0.1.0.0
 build-type:          Simple
 
+extra-source-files: include/*.h
+
 library
   exposed-modules:   MyLib
   hs-source-dirs:    src
   build-depends:     base
+  -- Where the linker looks for extra-libraries (none here); relative to the
+  -- package, like include-dirs.
+  extra-lib-dirs:    libs
   default-language:  Haskell2010
diff --git a/test/fixtures/basic/lib-pkg/libs/.gitkeep b/test/fixtures/basic/lib-pkg/libs/.gitkeep
new file mode 100644
--- /dev/null
+++ b/test/fixtures/basic/lib-pkg/libs/.gitkeep
diff --git a/test/fixtures/basic/lib-pkg/src/MyLib.hs-boot b/test/fixtures/basic/lib-pkg/src/MyLib.hs-boot
new file mode 100644
--- /dev/null
+++ b/test/fixtures/basic/lib-pkg/src/MyLib.hs-boot
@@ -0,0 +1,3 @@
+module MyLib (myLib) where
+
+myLib :: String
diff --git a/test/fixtures/custom-setup/dep/custom-dep-0.1/custom-dep.cabal b/test/fixtures/custom-setup/dep/custom-dep-0.1/custom-dep.cabal
--- a/test/fixtures/custom-setup/dep/custom-dep-0.1/custom-dep.cabal
+++ b/test/fixtures/custom-setup/dep/custom-dep-0.1/custom-dep.cabal
@@ -7,6 +7,8 @@
   setup-depends: base, Cabal
 
 library
-  exposed-modules:  Custom.Dep
+  exposed-modules:  Custom.Dep, Custom.Generated
+  -- What a real Setup.hs would generate.
+  autogen-modules:  Custom.Generated
   build-depends:    base
   default-language: Haskell2010
diff --git a/test/fixtures/pkgconfig/.buckconfig b/test/fixtures/pkgconfig/.buckconfig
new file mode 100644
--- /dev/null
+++ b/test/fixtures/pkgconfig/.buckconfig
@@ -0,0 +1,4 @@
+# Placeholder: this test only checks `cabal buck2`'s generated files, it
+# never actually invokes a real buck2 binary, so this doesn't need to be
+# a functional buck2 config - it only needs to exist so that
+# `ensureBuckconfigAndPackage` sees it's already there and leaves it alone.
diff --git a/test/fixtures/pkgconfig/PACKAGE b/test/fixtures/pkgconfig/PACKAGE
new file mode 100644
--- /dev/null
+++ b/test/fixtures/pkgconfig/PACKAGE
@@ -0,0 +1,1 @@
+# Placeholder - see .buckconfig.
diff --git a/test/fixtures/pkgconfig/buck2/.gitkeep b/test/fixtures/pkgconfig/buck2/.gitkeep
new file mode 100644
--- /dev/null
+++ b/test/fixtures/pkgconfig/buck2/.gitkeep
diff --git a/test/fixtures/pkgconfig/cabal.project b/test/fixtures/pkgconfig/cabal.project
new file mode 100644
--- /dev/null
+++ b/test/fixtures/pkgconfig/cabal.project
@@ -0,0 +1,3 @@
+packages: lib
+
+active-repositories: :none
diff --git a/test/fixtures/pkgconfig/lib/L.hs b/test/fixtures/pkgconfig/lib/L.hs
new file mode 100644
--- /dev/null
+++ b/test/fixtures/pkgconfig/lib/L.hs
@@ -0,0 +1,1 @@
+module L where
diff --git a/test/fixtures/pkgconfig/lib/lib.cabal b/test/fixtures/pkgconfig/lib/lib.cabal
new file mode 100644
--- /dev/null
+++ b/test/fixtures/pkgconfig/lib/lib.cabal
@@ -0,0 +1,12 @@
+cabal-version:     3.0
+name:              lib
+version:           0.1
+build-type:        Simple
+
+-- No C sources, so only what Cabal made of these is in the spec.
+library
+  exposed-modules:   L
+  build-depends:     base
+  pkgconfig-depends: foo
+  extra-lib-dirs:    plain
+  default-language:  Haskell2010
diff --git a/test/fixtures/pkgconfig/lib/plain/.gitkeep b/test/fixtures/pkgconfig/lib/plain/.gitkeep
new file mode 100644
--- /dev/null
+++ b/test/fixtures/pkgconfig/lib/plain/.gitkeep
diff --git a/test/fixtures/pkgconfig/pkgconfig/foo.pc b/test/fixtures/pkgconfig/pkgconfig/foo.pc
new file mode 100644
--- /dev/null
+++ b/test/fixtures/pkgconfig/pkgconfig/foo.pc
@@ -0,0 +1,6 @@
+prefix=/opt/foo
+Name: foo
+Description: A library that isn't there: only what pkg-config says about it matters
+Version: 1.2.3
+Cflags: -I/opt/foo/include -DFOO_ENABLED
+Libs: -L/opt/foo/lib -lfoo -Wl,-rpath,/opt/foo/lib
diff --git a/test/fixtures/same-name/.buckconfig b/test/fixtures/same-name/.buckconfig
new file mode 100644
--- /dev/null
+++ b/test/fixtures/same-name/.buckconfig
@@ -0,0 +1,4 @@
+# Placeholder: this test only checks `cabal buck2`'s generated files, it
+# never actually invokes a real buck2 binary, so this doesn't need to be
+# a functional buck2 config - it only needs to exist so that
+# `ensureBuckconfigAndPackage` sees it's already there and leaves it alone.
diff --git a/test/fixtures/same-name/PACKAGE b/test/fixtures/same-name/PACKAGE
new file mode 100644
--- /dev/null
+++ b/test/fixtures/same-name/PACKAGE
@@ -0,0 +1,1 @@
+# Placeholder - see .buckconfig.
diff --git a/test/fixtures/same-name/buck2/.gitkeep b/test/fixtures/same-name/buck2/.gitkeep
new file mode 100644
--- /dev/null
+++ b/test/fixtures/same-name/buck2/.gitkeep
diff --git a/test/fixtures/same-name/cabal.project b/test/fixtures/same-name/cabal.project
new file mode 100644
--- /dev/null
+++ b/test/fixtures/same-name/cabal.project
@@ -0,0 +1,3 @@
+packages: tool user
+
+active-repositories: :none
diff --git a/test/fixtures/same-name/tool/app/Main.hs b/test/fixtures/same-name/tool/app/Main.hs
new file mode 100644
--- /dev/null
+++ b/test/fixtures/same-name/tool/app/Main.hs
@@ -0,0 +1,6 @@
+module Main (main) where
+
+import Tool (greeting)
+
+main :: IO ()
+main = putStrLn greeting
diff --git a/test/fixtures/same-name/tool/src/Tool.hs b/test/fixtures/same-name/tool/src/Tool.hs
new file mode 100644
--- /dev/null
+++ b/test/fixtures/same-name/tool/src/Tool.hs
@@ -0,0 +1,4 @@
+module Tool (greeting) where
+
+greeting :: String
+greeting = "hello"
diff --git a/test/fixtures/same-name/tool/tool.cabal b/test/fixtures/same-name/tool/tool.cabal
new file mode 100644
--- /dev/null
+++ b/test/fixtures/same-name/tool/tool.cabal
@@ -0,0 +1,19 @@
+cabal-version:      2.4
+name:               tool
+version:            0.1.0.0
+build-type:         Simple
+
+-- A library and an executable with the package's own name: a common layout
+-- (the executable is a thin wrapper around the library), but a buck2 target
+-- name has to be unique within its package.
+library
+  exposed-modules:   Tool
+  hs-source-dirs:    src
+  build-depends:     base
+  default-language:  Haskell2010
+
+executable tool
+  main-is:           Main.hs
+  hs-source-dirs:    app
+  build-depends:     base, tool
+  default-language:  Haskell2010
diff --git a/test/fixtures/same-name/user/src/User.hs b/test/fixtures/same-name/user/src/User.hs
new file mode 100644
--- /dev/null
+++ b/test/fixtures/same-name/user/src/User.hs
@@ -0,0 +1,6 @@
+module User (user) where
+
+import Tool (greeting)
+
+user :: String
+user = greeting ++ " from user"
diff --git a/test/fixtures/same-name/user/user.cabal b/test/fixtures/same-name/user/user.cabal
new file mode 100644
--- /dev/null
+++ b/test/fixtures/same-name/user/user.cabal
@@ -0,0 +1,13 @@
+cabal-version:      2.4
+name:               user
+version:            0.1.0.0
+build-type:         Simple
+
+-- Needs the other package's executable on PATH when it builds, under the
+-- name Cabal knows it by.
+library
+  exposed-modules:   User
+  hs-source-dirs:    src
+  build-depends:     base, tool
+  build-tool-depends: tool:tool
+  default-language:  Haskell2010
