diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,26 @@
+Copyright 2018-2019 Isumi Feng
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimer in the documentation and/or
+other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its contributors
+may be used to endorse or promote products derived from this software without
+specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/cabal-bundle-clib.cabal b/cabal-bundle-clib.cabal
new file mode 100644
--- /dev/null
+++ b/cabal-bundle-clib.cabal
@@ -0,0 +1,38 @@
+cabal-version:       2.4
+-- Initial package description 'cabal-bundle-clib.cabal' generated by
+-- 'cabal init'.  For further documentation, see
+-- http://haskell.org/cabal/users-guide/
+
+name:                cabal-bundle-clib
+version:             0.1.0
+synopsis:            Bundling C/C++ projects in Cabal package made easy
+license:             BSD-3-Clause
+license-file:        LICENSE
+author:              Isumi Feng
+maintainer:          contact@zelinf.net
+copyright:           2019, Isumi Feng
+category:            Development
+stability:           alpha
+homepage:            https://github.com/isumif/cabal-bundle-clib
+bug-reports:         https://github.com/isumif/cabal-bundle-clib/issues
+description:         Please see the [README](https://github.com/isumif/cabal-bundle-clib)
+extra-source-files:       CHANGELOG.md
+
+library
+  hs-source-dirs: src
+  default-language: Haskell2010
+  ghc-options: -Wall
+  exposed-modules:
+      Development.CabalBundleCLib
+    , Development.CabalBundleCLib.Types
+    , Development.CabalBundleCLib.CMake
+  build-depends:
+      base ^>=4.12.0.0
+    , Cabal ^>=2.4.1.0
+    , text
+    , time
+    , temporary
+    , directory
+    , process
+    , filepath
+    , bytestring
diff --git a/src/Development/CabalBundleCLib.hs b/src/Development/CabalBundleCLib.hs
new file mode 100644
--- /dev/null
+++ b/src/Development/CabalBundleCLib.hs
@@ -0,0 +1,94 @@
+module Development.CabalBundleCLib
+  ( mainWithCLib
+  , module Development.CabalBundleCLib.Types
+  ) where
+
+import           Data.Time.Clock                            (getCurrentTime)
+import           Development.CabalBundleCLib.Types
+import qualified Distribution.Compat.Lens                   as Lens
+import qualified Distribution.PackageDescription            as Cabal hiding
+                                                                      (Flag)
+import qualified Distribution.Simple                        as Cabal
+import qualified Distribution.Simple.Setup                  as Cabal
+import qualified Distribution.Types.BuildInfo.Lens          as CabalLens
+import qualified Distribution.Types.Library.Lens            as CabalLens
+import qualified Distribution.Types.LocalBuildInfo          as Cabal
+import qualified Distribution.Types.PackageDescription.Lens as CabalLens
+import           System.Directory                           (removeFile)
+import           System.FilePath                            ((</>))
+import           System.IO.Temp                             (writeTempFile)
+
+mainWithCLib :: FilePath -- ^c project root
+             -> [String] -- ^bundled libraries
+             -> [FilePath] -- ^parent dirs of libraries (relative to build root)
+             -> (BuildAction -> BuildDirs -> IO ()) -- ^builder
+             -- The three 'FilePath's are source dir, build dir and installation
+             -- dir, respectively
+             -> IO ()
+mainWithCLib cProjRoot bundledLibs bundledLibDirs builder =
+  Cabal.defaultMainWithHooks Cabal.simpleUserHooks
+    { Cabal.confHook = customConfigure bundledLibs
+    , Cabal.postConf = \_ _ _ _ -> pure () -- remove the check for foreign libs
+    , Cabal.buildHook = customBuild cProjRoot bundledLibs bundledLibDirs builder
+    }
+
+customConfigure :: [String]
+                -> (Cabal.GenericPackageDescription, Cabal.HookedBuildInfo)
+                -> Cabal.ConfigFlags
+                -> IO Cabal.LocalBuildInfo
+customConfigure bundledLibs gpkgDesc configFlags = do
+  lbi <- Cabal.confHook Cabal.simpleUserHooks gpkgDesc configFlags
+  let localPkgDescr = Cabal.localPkgDescr lbi
+      localPkgDescr' = Lens.over CabalLens.library updateLibrary localPkgDescr
+  pure $ lbi { Cabal.localPkgDescr = localPkgDescr' }
+  where
+    updateLibrary :: Maybe Cabal.Library -> Maybe Cabal.Library
+    updateLibrary = fmap $ Lens.over CabalLens.libBuildInfo updateBuildInfo
+    updateBuildInfo :: Cabal.BuildInfo -> Cabal.BuildInfo
+    updateBuildInfo bi = bi
+      { Cabal.extraLibs = bundledLibs ++ Cabal.extraLibs bi
+      , Cabal.extraBundledLibs = bundledLibs ++ Cabal.extraBundledLibs bi
+      }
+
+customBuild :: FilePath -- ^c project root
+            -> [String] -- ^bundled libs
+            -> [FilePath] -- bundled lib dirs
+            -> (BuildAction -> BuildDirs -> IO ())
+            -> Cabal.PackageDescription
+            -> Cabal.LocalBuildInfo
+            -> Cabal.UserHooks
+            -> Cabal.BuildFlags
+            -> IO ()
+customBuild cProjRoot bundledLibs bundledLibDirs builder packageDesc localBuildInfo userHooks buildFlags = do
+  currentTime <- getCurrentTime
+  let versionInfo = "const char *clibver = \"" ++ show currentTime ++ "\";\n"
+  let buildDir = Cabal.buildDir localBuildInfo
+  clibVersionFile <- writeTempFile buildDir "clibver.c" versionInfo
+  let updateLibrary :: Cabal.Library -> Cabal.Library
+      updateLibrary lib =
+        let lib' = Lens.over (CabalLens.libBuildInfo . CabalLens.cSources) (clibVersionFile :) lib
+            lib'' = Lens.over (CabalLens.libBuildInfo . CabalLens.extraLibDirs)
+              ((fmap (\dir -> buildDir </> dir) bundledLibDirs) ++) lib'
+         in Lens.over (CabalLens.libBuildInfo . CabalLens.extraLibs) (bundledLibs ++) lib''
+  let packageDesc' = Lens.over CabalLens.library (fmap updateLibrary) packageDesc
+  builder
+    (BuildActionBuild (getBuildMode localBuildInfo))
+    (BuildDirs cProjRoot (buildDir </> "clibbuild") buildDir) -- TODO use some unique build dir name
+  simpleBuildHook packageDesc' localBuildInfo userHooks buildFlags
+  removeFile clibVersionFile
+
+getBuildMode :: Cabal.LocalBuildInfo -> BuildMode
+getBuildMode localBuildInfo =
+  case Cabal.configOptimization . Cabal.configFlags $ localBuildInfo of
+    Cabal.Flag level ->
+      case level of
+        Cabal.MaximumOptimisation -> BuildModeRelease
+        _                         -> BuildModeDebug
+    _ -> BuildModeDebug
+
+simpleBuildHook :: Cabal.PackageDescription
+                -> Cabal.LocalBuildInfo
+                -> Cabal.UserHooks
+                -> Cabal.BuildFlags
+                -> IO ()
+simpleBuildHook = Cabal.buildHook Cabal.simpleUserHooks
diff --git a/src/Development/CabalBundleCLib/CMake.hs b/src/Development/CabalBundleCLib/CMake.hs
new file mode 100644
--- /dev/null
+++ b/src/Development/CabalBundleCLib/CMake.hs
@@ -0,0 +1,32 @@
+{-# LANGUAGE RecordWildCards #-}
+
+module Development.CabalBundleCLib.CMake
+  ( simpleCMakeBuilder
+  ) where
+
+import           Development.CabalBundleCLib.Types
+import           System.Directory                  (withCurrentDirectory)
+import           System.FilePath                   ((</>))
+import           System.Process                    (callCommand, callProcess)
+
+simpleCMakeBuilder :: BuildAction -> BuildDirs -> IO ()
+simpleCMakeBuilder BuildActionClean BuildDirs{..} =
+  withCurrentDirectory buildDirsBuild $
+    callProcess "make" ["clean"]
+simpleCMakeBuilder (BuildActionBuild mode) BuildDirs{..} = do
+  callProcess "cmake" ["-S", buildDirsSource, "-B", buildDirsBuild, (buildModeToFlag mode)]
+  callProcess "cmake" ["--build", buildDirsBuild]
+  callCommand $ "cp " ++ buildDirsBuild </> "*.a" ++ " " ++ buildDirsInstall
+
+buildModeToFlag :: BuildMode -> String
+buildModeToFlag buildMode = "-DCMAKE_BUILD_TYPE=" ++ mode
+  where
+    mode = case buildMode of
+             BuildModeDebug   -> "Debug"
+             BuildModeRelease -> "Release"
+
+{-
+cmake -S $SOURCE_DIR -B $BUILD_DIR
+cmake --build $BUILD_DIR
+cp $BUILD_DIR/*.a $BUILD_DIR/*.so.* $INSTALL_DIR
+-}
diff --git a/src/Development/CabalBundleCLib/Types.hs b/src/Development/CabalBundleCLib/Types.hs
new file mode 100644
--- /dev/null
+++ b/src/Development/CabalBundleCLib/Types.hs
@@ -0,0 +1,23 @@
+module Development.CabalBundleCLib.Types
+  ( BuildAction(..)
+  , BuildMode(..)
+  , BuildDirs(..)
+  , Builder
+  ) where
+
+-- |Type for build actions.
+data BuildAction =
+    BuildActionBuild BuildMode
+  | BuildActionClean
+  deriving (Show, Eq)
+
+data BuildMode = BuildModeDebug | BuildModeRelease
+  deriving (Show, Eq)
+
+data BuildDirs = BuildDirs
+  { buildDirsSource  :: FilePath
+  , buildDirsBuild   :: FilePath
+  , buildDirsInstall :: FilePath
+  } deriving (Show, Eq)
+
+type Builder = BuildAction -> BuildDirs -> IO ()
