diff --git a/hix.cabal b/hix.cabal
--- a/hix.cabal
+++ b/hix.cabal
@@ -5,7 +5,7 @@
 -- see: https://github.com/sol/hpack
 
 name:           hix
-version:        0.6.2
+version:        0.6.7
 synopsis:       Haskell/Nix development build tools
 description:    See https://hackage.haskell.org/package/hix/docs/Hix.html
 category:       Build
diff --git a/lib/Hix/Bootstrap.hs b/lib/Hix/Bootstrap.hs
--- a/lib/Hix/Bootstrap.hs
+++ b/lib/Hix/Bootstrap.hs
@@ -133,11 +133,11 @@
 
 renderExpr :: Int -> Expr -> NonEmpty Text
 renderExpr ind = \case
-  ExprString s -> indent ind [[exon|"#{s}"|]]
+  ExprString s -> indent ind [[exon|"#{Text.replace "\"" "\\\"" s}"|]]
   ExprLit e -> [e]
   ExprList l -> "[" :| (indent (ind + 2) (toList . renderExpr ind =<< l)) ++ ["]"]
   ExprAttrs a -> case renderAttrs ind a of
-    [] -> ["{};"]
+    [] -> ["{}"]
     as -> "{" :| indent (ind + 2) as ++ ["}"]
   ExprPrefix p (renderExpr ind -> h :| t) ->
     [exon|#{p} #{h}|] :| t
@@ -235,6 +235,17 @@
 mkAttrs a e =
   (fmap ($ e) a)
 
+notNil :: ExprAttr -> Bool
+notNil = \case
+  ExprAttrNil -> False
+  _ -> True
+
+nonEmptyAttrs :: Text -> [ExprAttr] -> ExprAttr
+nonEmptyAttrs key =
+  filter notNil >>> \case
+    [] -> ExprAttrNil
+    as -> ExprAttr key (ExprAttrs as)
+
 -- TODO extract version and put it in a file
 knownPackageKeys :: PackageDescription -> [ExprAttr]
 knownPackageKeys =
@@ -243,7 +254,8 @@
     single "build-type" buildType,
     single "copyright" (.copyright),
     single "license" license,
-    singleOpt "license-file" (head . licenseFiles)
+    singleOpt "license-file" (head . licenseFiles),
+    single "version" (.package.pkgVersion)
   ]
 
 metaPackageKeys :: PackageDescription -> [ExprAttr]
@@ -274,14 +286,22 @@
         multi "default-extensions" (.defaultExtensions),
         multiOrSingle "source-dirs" (.hsSourceDirs),
         singleOpt "language" (.defaultLanguage),
-        multi "ghc-options" (filter notDefaultGhcOption . ghcFlavour . (.options))
+        multi "ghc-options" (filter notDefaultGhcOption . ghcFlavour . (.options)),
+        misc
       ] info
+
+    misc e =
+      nonEmptyAttrs "component" (mkAttrs [
+        multi "other-modules" (.otherModules)
+      ] e)
+
     (preludeWithVersion, deps)
       | Just p <- prelude =
         let (v, res) = foldl (amendPrelude p) (Nothing, []) info.targetBuildDepends
         in (Just (PreludeWithVersion p v), res)
       | otherwise =
-        (Nothing, filter notBase (info.targetBuildDepends))
+        (Nothing, filter notBase info.targetBuildDepends)
+
     amendPrelude p (Nothing, ds) dep@(Dependency (Cabal.unPackageName -> dname) _ _) | dname == p.preludePackage =
       (Just dep, ds)
     amendPrelude _ (v, ds) d = (v, d : ds)
@@ -291,28 +311,32 @@
   Cabal.Dependency "base" _ _ -> False
   _ -> True
 
-convertComponent :: ComponentType -> BuildInfo -> HixComponent
-convertComponent special info =
-  HixComponent {..}
+convertComponent :: ComponentType -> BuildInfo -> [ExprAttr] -> HixComponent
+convertComponent special info extra =
+  HixComponent {known = knownCommon <> extra, ..}
   where
-    (prelude, known) = knownComponentKeys preludeBasic info
+    (prelude, knownCommon) = knownComponentKeys preludeBasic info
     preludeBasic = findPrelude info.mixins
 
 convertLibrary :: Cabal.Library -> HixComponent
 convertLibrary lib =
-  convertComponent Library lib.libBuildInfo
+  convertComponent Library lib.libBuildInfo extra
+  where
+    extra = mkAttrs [
+      multi "reexported-modules" (.reexportedModules)
+      ] lib
 
 convertExecutable :: UnqualComponentName -> Cabal.Executable -> HixComponent
 convertExecutable name exe =
-  convertComponent (Executable (toText (unUnqualComponentName name))) exe.buildInfo
+  convertComponent (Executable (toText (unUnqualComponentName name))) exe.buildInfo []
 
 convertTestsuite :: UnqualComponentName -> Cabal.TestSuite -> HixComponent
 convertTestsuite name test =
-  convertComponent (Test (toText (unUnqualComponentName name))) test.testBuildInfo
+  convertComponent (Test (toText (unUnqualComponentName name))) test.testBuildInfo []
 
 convertBenchmark :: UnqualComponentName -> Cabal.Benchmark -> HixComponent
 convertBenchmark name bench =
-  convertComponent (Benchmark (toText (unUnqualComponentName name))) bench.benchmarkBuildInfo
+  convertComponent (Benchmark (toText (unUnqualComponentName name))) bench.benchmarkBuildInfo []
 
 convert :: CabalInfo -> HixPackage
 convert cinfo =
@@ -340,7 +364,7 @@
 renderComponent HixComponent {..} =
   ExprAttr key cabalConfig
   where
-    cabalConfig = ExprAttrs (known <> foldMap preludeAttrs prelude)
+    cabalConfig = ExprAttrs (enable <> foldMap preludeAttrs prelude <> known)
     preludeAttrs p =
       [ExprAttr "prelude" (ExprAttrs [
         ExprAttr "package" (preludePackageAttrs p),
@@ -358,6 +382,9 @@
       Executable name -> [exon|executables.#{name}|]
       Test name -> [exon|tests.#{name}|]
       Benchmark name -> [exon|benchmarks.#{name}|]
+    enable = case special of
+      Library -> [ExprAttr "enable" (ExprLit "true")]
+      _ -> []
 
 flakePackage :: HixPackage -> ExprAttr
 flakePackage pkg =
diff --git a/lib/Hix/Component.hs b/lib/Hix/Component.hs
--- a/lib/Hix/Component.hs
+++ b/lib/Hix/Component.hs
@@ -50,9 +50,19 @@
 packageByDir config dir =
   noteEnv [exon|No package at this directory: #{pathText dir}|] (tryPackageByDir config dir)
 
-packageDefault :: PackagesConfig -> ResolvedPackage
-packageDefault = \case
+packageDefault :: Maybe PackageName -> PackagesConfig -> ResolvedPackage
+packageDefault mainPkg = \case
+  [] -> NoPackage "Project has no packages."
   [(_, pkg)] -> ResolvedPackage False pkg
+  pkgs | Just name <- mainPkg ->
+    case Map.lookup name pkgs of
+      Just pkg ->
+        ResolvedPackage False pkg
+      Nothing ->
+        NoPackage (
+          [exon|Project has multiple packages and the main package '##{name}' is not among them. |] <>
+          "Specify -p or -f to choose one explicitly."
+        )
   _ -> NoPackage "Project has more than one package, specify -p or -f."
 
 packageForSpec ::
@@ -75,12 +85,13 @@
 
 packageForSpecOrDefault ::
   Path Abs Dir ->
+  Maybe PackageName ->
   PackagesConfig ->
   Maybe PackageSpec ->
   M ResolvedPackage
-packageForSpecOrDefault root config = \case
+packageForSpecOrDefault root mainPkg config = \case
   Just pkg -> ResolvedPackage True <$> packageForSpec root config pkg
-  Nothing -> pure (packageDefault config)
+  Nothing -> pure (packageDefault mainPkg config)
 
 matchComponent :: ComponentConfig -> ComponentSpec -> Bool
 matchComponent candidate (ComponentSpec name dir) =
@@ -126,11 +137,12 @@
 
 targetForComponent ::
   Path Abs Dir ->
+  Maybe PackageName ->
   PackagesConfig ->
   ComponentCoords ->
   M TargetOrDefault
-targetForComponent root config spec = do
-  package <- packageForSpecOrDefault root config spec.package
+targetForComponent root mainPkg config spec = do
+  package <- packageForSpecOrDefault root mainPkg config spec.package
   targetInPackage package spec.component
 
 targetForFile ::
@@ -156,31 +168,34 @@
 
 targetComponentIn ::
   Path Abs Dir ->
+  Maybe PackageName ->
   PackagesConfig ->
   TargetSpec ->
   M TargetOrDefault
-targetComponentIn root config = \case
+targetComponentIn root mainPkg config = \case
   TargetForComponent spec ->
-    targetForComponent root config spec
+    targetForComponent root mainPkg config spec
   TargetForFile spec ->
     ExplicitTarget <$> targetForFile root config spec
 
 targetComponent ::
   Maybe (Path Abs Dir) ->
+  Maybe PackageName ->
   PackagesConfig ->
   TargetSpec ->
   M TargetOrDefault
-targetComponent cliRoot config spec = do
+targetComponent cliRoot mainPkg config spec = do
   root <- rootDir cliRoot
-  targetComponentIn root config spec
+  targetComponentIn root mainPkg config spec
 
 targetComponentOrError ::
   Maybe (Path Abs Dir) ->
+  Maybe PackageName ->
   PackagesConfig ->
   TargetSpec ->
   M Target
-targetComponentOrError cliRoot config spec =
-  targetComponent cliRoot config spec >>= \case
+targetComponentOrError cliRoot mainPkg config spec =
+  targetComponent cliRoot mainPkg config spec >>= \case
     ExplicitTarget t -> pure t
     DefaultTarget t -> pure t
     NoDefaultTarget err -> throwM (EnvError err)
diff --git a/lib/Hix/Data/GhciConfig.hs b/lib/Hix/Data/GhciConfig.hs
--- a/lib/Hix/Data/GhciConfig.hs
+++ b/lib/Hix/Data/GhciConfig.hs
@@ -3,7 +3,7 @@
 import Data.Aeson (FromJSON, FromJSONKey)
 import GHC.Exts (IsList)
 
-import Hix.Data.ComponentConfig (EnvRunner, PackagesConfig)
+import Hix.Data.ComponentConfig (EnvRunner, PackageName, PackagesConfig)
 
 newtype RunnerName =
   RunnerName { unRunnerName :: Text }
@@ -32,7 +32,8 @@
 data EnvConfig =
   EnvConfig {
     packages :: PackagesConfig,
-    defaultEnv :: EnvRunner
+    defaultEnv :: EnvRunner,
+    mainPackage :: Maybe PackageName
   }
   deriving stock (Eq, Show, Generic)
   deriving anyclass (FromJSON)
@@ -40,6 +41,7 @@
 data GhciConfig =
   GhciConfig {
     packages :: PackagesConfig,
+    mainPackage :: Maybe PackageName,
     setup :: Map RunnerName GhciSetupCode,
     run :: Map RunnerName GhciRunExpr,
     args :: GhciArgs
diff --git a/lib/Hix/Data/GhciTest.hs b/lib/Hix/Data/GhciTest.hs
--- a/lib/Hix/Data/GhciTest.hs
+++ b/lib/Hix/Data/GhciTest.hs
@@ -18,7 +18,8 @@
     test :: GhciTest,
     shell :: Text,
     run :: Maybe Text,
-    scriptFile :: Path Abs File
+    scriptFile :: Path Abs File,
+    cmdline :: Text
   }
   deriving stock (Eq, Show, Generic)
 
diff --git a/lib/Hix/Env.hs b/lib/Hix/Env.hs
--- a/lib/Hix/Env.hs
+++ b/lib/Hix/Env.hs
@@ -5,7 +5,12 @@
 
 import Hix.Component (targetComponent)
 import qualified Hix.Data.ComponentConfig
-import Hix.Data.ComponentConfig (EnvRunner (EnvRunner), PackagesConfig, TargetOrDefault (DefaultTarget, ExplicitTarget))
+import Hix.Data.ComponentConfig (
+  EnvRunner (EnvRunner),
+  PackageName,
+  PackagesConfig,
+  TargetOrDefault (DefaultTarget, ExplicitTarget),
+  )
 import Hix.Data.Error (pathText)
 import qualified Hix.Data.GhciConfig
 import Hix.Json (jsonConfig)
@@ -17,11 +22,12 @@
 -- Nothing when the config requests it
 componentRunner ::
   Maybe (Path Abs Dir) ->
+  Maybe PackageName ->
   PackagesConfig ->
   TargetSpec ->
   M (Maybe EnvRunner)
-componentRunner cliRoot config spec =
-  targetComponent cliRoot config spec <&> \case
+componentRunner cliRoot defaultPkg config spec =
+  targetComponent cliRoot defaultPkg config spec <&> \case
     ExplicitTarget t -> t.component.runner
     DefaultTarget t -> t.component.runner
     _ -> Nothing
@@ -29,7 +35,8 @@
 envRunner :: EnvRunnerOptions -> M EnvRunner
 envRunner opts = do
   config <- either pure jsonConfig opts.config
-  fromMaybe config.defaultEnv . join <$> traverse (componentRunner opts.root config.packages) opts.component
+  let runner = componentRunner opts.root config.mainPackage config.packages
+  fromMaybe config.defaultEnv . join <$> traverse runner opts.component
 
 printEnvRunner :: EnvRunnerOptions -> M ()
 printEnvRunner opts = do
diff --git a/lib/Hix/Ghci.hs b/lib/Hix/Ghci.hs
--- a/lib/Hix/Ghci.hs
+++ b/lib/Hix/Ghci.hs
@@ -32,7 +32,7 @@
 import Hix.Monad (M, noteGhci)
 import qualified Hix.Options as Options
 import Hix.Options (
-  ExtraGhciOptions,
+  ExtraGhciOptions (ExtraGhciOptions),
   ExtraGhcidOptions (ExtraGhcidOptions),
   GhciOptions (GhciOptions),
   GhcidOptions,
@@ -110,7 +110,7 @@
 assemble opt = do
   config <- either pure jsonConfig opt.config
   root <- rootDir opt.root
-  Target {..} <- targetComponentOrError opt.root config.packages opt.component
+  Target {..} <- targetComponentOrError opt.root config.mainPackage config.packages opt.component
   script <- ghciScript config package sourceDir opt
   pure GhciTest {
     script,
@@ -139,9 +139,16 @@
     hClose handle
     pure path
 
+argFrag :: Text -> Text
+argFrag "" = ""
+argFrag s = [exon| #{s}|]
+
+optArg :: Maybe Text -> Text
+optArg = foldMap argFrag
+
 searchPathArg :: NonEmpty (Path Abs Dir) -> Text
 searchPathArg paths =
-  [exon| -i#{colonSeparated}|]
+  [exon|-i#{colonSeparated}|]
   where
     colonSeparated = Text.intercalate ":" (pathText <$> toList paths)
 
@@ -154,10 +161,12 @@
 ghciCmdline test extra scriptFile runScriptFile =
   GhciRun {..}
   where
-    shell = [exon|##{Text.unwords (coerce test.args)}#{sp} -ghci-script=##{toFilePath scriptFile}#{extraOpts}|]
+    cmdline = [exon|ghci#{shell}#{optArg run}|]
+    shell = [exon|#{argFrag args}#{argFrag sp} -ghci-script=##{toFilePath scriptFile}#{argFrag extraOpts}|]
+    args = Text.unwords (coerce test.args)
     run = runScriptFile <&> \ f -> [exon|-ghci-script=##{toFilePath f}|]
     sp = foldMap searchPathArg (nonEmpty test.searchPath)
-    extraOpts | Just o <- extra = [exon| ##{o}|]
+    extraOpts | Just (ExtraGhciOptions o) <- extra = o
               | otherwise = ""
 
 ghciCmdlineFromOptions ::
@@ -178,7 +187,7 @@
   ghci <- ghciCmdlineFromOptions tmp opt.ghci
   let
     test = fromMaybe "main" ghci.test.test
-  pure (GhcidRun [exon|ghcid --command="ghci #{ghci.shell}" --test='##{test}'#{foldMap extra opt.extra}|] ghci)
+  pure (GhcidRun [exon|ghcid --command="ghci#{ghci.shell}" --test='##{test}'#{foldMap extra opt.extra}|] ghci)
   where
     extra (ExtraGhcidOptions o) = [exon| ##{o}|]
 
diff --git a/lib/Hix/Preproc.hs b/lib/Hix/Preproc.hs
--- a/lib/Hix/Preproc.hs
+++ b/lib/Hix/Preproc.hs
@@ -9,6 +9,7 @@
 import qualified Data.ByteString.Builder as ByteStringBuilder
 import Data.ByteString.Builder (Builder, byteString, charUtf8, stringUtf8)
 import Data.Generics.Labels ()
+import qualified Data.Map.Strict as Map
 import Distribution.PackageDescription (BuildInfo (..))
 import Distribution.Simple (PerCompilerFlavor (PerCompilerFlavor))
 import qualified Exon
@@ -34,7 +35,6 @@
 import Hix.Optparse (JsonConfig)
 import qualified Hix.Prelude as Prelude
 import Hix.Prelude (Prelude (Prelude), findPrelude)
-import qualified Data.Map.Strict as Map
 
 type Regex = IndexedTraversal' Int ByteString Match
 
@@ -461,7 +461,7 @@
   M CabalConfig
 fromConfig cliRoot source pconf = do
   conf <- either pure jsonConfig pconf
-  target <- targetComponentOrError cliRoot conf.packages (TargetForFile source)
+  target <- targetComponentOrError cliRoot Nothing conf.packages (TargetForFile source)
   pure CabalConfig {
     extensions = stringUtf8 <$> target.component.language : target.component.extensions,
     ghcOptions = stringUtf8 <$> target.component.ghcOptions,
diff --git a/test/Hix/Test/BootstrapTest.hs b/test/Hix/Test/BootstrapTest.hs
--- a/test/Hix/Test/BootstrapTest.hs
+++ b/test/Hix/Test/BootstrapTest.hs
@@ -19,7 +19,7 @@
 
 name:           red-panda
 version:        0.1.0.0
-synopsis:       A Haskell project
+synopsis:       A Haskell "project"
 description:    See https://hackage.haskell.org/package/red-panda/docs/RedPanda.html
 author:         Panda
 maintainer:     Panda
@@ -31,6 +31,7 @@
   exposed-modules:
       RedPanda
   other-modules:
+      NormalPanda
       Paths_red_panda
   autogen-modules:
       Paths_red_panda
@@ -49,13 +50,12 @@
       base hiding (Prelude)
     , incipit-base (IncipitBase as Prelude)
     , incipit-base hiding (IncipitBase)
+  reexported-modules:
+      Control.Monad
+    , Data.Maybe
 
 executable red-panda
   main-is: Main.hs
-  other-modules:
-      Paths_red_panda
-  autogen-modules:
-      Paths_red_panda
   hs-source-dirs:
       app
   ghc-options: -threaded -rtsopts -with-rtsopts=-N -Wall
@@ -69,9 +69,6 @@
   main-is: Main.hs
   other-modules:
       RedPanda.Test.NameTest
-      Paths_red_panda
-  autogen-modules:
-      Paths_red_panda
   hs-source-dirs:
       test
   ghc-options: -threaded -rtsopts -with-rtsopts=-N -Wall
@@ -103,12 +100,21 @@
           build-type = "Simple";
           license = "BSD-2-Clause-Patent";
           license-file = "LICENSE";
+          version = "0.1.0.0";
           meta = {
             maintainer = "Panda";
-            synopsis = "A Haskell project";
+            synopsis = "A Haskell \"project\"";
           };
         };
         library = {
+          enable = true;
+          prelude = {
+            package = {
+              name = "incipit-base";
+              version = ">=0.4 && <0.6";
+            };
+            module = "IncipitBase";
+          };
           dependencies = [
             "containers"
             "base >=4 && <5"
@@ -122,13 +128,16 @@
           ghc-options = [
             "-Wall"
           ];
-          prelude = {
-            package = {
-              name = "incipit-base";
-              version = ">=0.4 && <0.6";
-            };
-            module = "IncipitBase";
+          component = {
+            other-modules = [
+              "NormalPanda"
+              "Paths_red_panda"
+            ];
           };
+          reexported-modules = [
+            "Control.Monad"
+            "Data.Maybe"
+          ];
         };
         executables.red-panda = {
           dependencies = [
@@ -152,6 +161,11 @@
           ghc-options = [
             "-Wall"
           ];
+          component = {
+            other-modules = [
+              "RedPanda.Test.NameTest"
+            ];
+          };
         };
       };
     };
diff --git a/test/Hix/Test/GhciTest.hs b/test/Hix/Test/GhciTest.hs
--- a/test/Hix/Test/GhciTest.hs
+++ b/test/Hix/Test/GhciTest.hs
@@ -19,7 +19,7 @@
 import Hix.Data.GhciConfig (ChangeDir (ChangeDir), EnvConfig (EnvConfig), GhciConfig (..))
 import qualified Hix.Data.GhciTest as GhciTest
 import Hix.Env (envRunner)
-import Hix.Ghci (assemble, ghcidCmdlineFromOptions)
+import Hix.Ghci (assemble, ghciCmdlineFromOptions, ghcidCmdlineFromOptions)
 import Hix.Monad (runM)
 import qualified Hix.Options as Options
 import Hix.Options (
@@ -95,8 +95,9 @@
   GhciOptions {
     config = Left GhciConfig {
       packages,
+      mainPackage = Nothing,
       setup = [("generic", "import Test.Tasty")],
-      run = [("generic", ("check . property . test"))],
+      run = [("generic", "check . property . test")],
       args = ["-Werror"]
     },
     root = Nothing,
@@ -132,6 +133,44 @@
   cmdline <- evalEither res
   ghcidTarget root cmdline.ghci.scriptFile === cmdline.cmdline
 
+mainOptions :: GhciOptions
+mainOptions =
+  GhciOptions {
+    config = Left GhciConfig {
+      packages,
+      mainPackage = Just "core",
+      setup = [("generic", "import Test.Tasty")],
+      run = [("generic", "")],
+      args = []
+    },
+    root = Nothing,
+    component = TargetForComponent (ComponentCoords Nothing Nothing),
+    test = TestOptions {
+      mod = "Main",
+      test = Nothing,
+      runner = Nothing,
+      cd = ChangeDir True
+    },
+    extra = Nothing
+  }
+
+mainPackageTarget ::
+  Path Abs Dir ->
+  Path Abs File ->
+  Text
+mainPackageTarget cwd scriptFile =
+  [exon|ghci -i#{path} -ghci-script=#{pathText scriptFile}|]
+  where
+    path = [exon|#{dir}packages/core/test/:#{dir}packages/api/lib/:#{dir}packages/core/lib/|]
+    dir = pathText cwd
+
+test_mainPackage :: TestT IO ()
+test_mainPackage = do
+  res <- lift $ withSystemTempDir "hix-test" \ tmp ->
+    runM root (ghciCmdlineFromOptions tmp mainOptions)
+  cmdline <- evalEither res
+  mainPackageTarget root cmdline.scriptFile === cmdline.cmdline
+
 spec2 :: TargetSpec
 spec2 =
   TargetForFile (root </> [relfile|packages/core/test/Main.hs|])
@@ -148,7 +187,7 @@
   res <- evalEither =<< liftIO (runM root (envRunner conf))
   target === res
   where
-    conf = EnvRunnerOptions (Left (EnvConfig packages defaultRunner)) Nothing (Just spec)
+    conf = EnvRunnerOptions (Left (EnvConfig packages defaultRunner Nothing)) Nothing (Just spec)
 
 test_componentEnv :: TestT IO ()
 test_componentEnv = do
