|
| 1 | +.. _Custom: |
| 2 | + |
| 3 | +Custom Package Formats |
| 4 | +====================== |
| 5 | + |
| 6 | +This section provides an overview of different pacakging flavors. |
| 7 | + |
| 8 | +SBT Assembly |
| 9 | +------------ |
| 10 | + |
| 11 | + **Main Goal** |
| 12 | + |
| 13 | + | Create a fat-jar with sbt-assembly in order to deliever a single, |
| 14 | + | self-containing jar as a package instead of the default lib/ structure |
| 15 | +
|
| 16 | +First add the sbt-assembly plugin to your `plugins.sbt` file. |
| 17 | + |
| 18 | +.. code-block:: scala |
| 19 | +
|
| 20 | + addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.11.2") |
| 21 | + |
| 22 | +The next step is to remove all the jar mappings from the normal mappings and only add the |
| 23 | +assembly jar. In this example we'll set the assembly jar name ourself, so we know exactly |
| 24 | +what the output should look like. Finally we change the ``scriptClasspath`` so it only |
| 25 | +contains the assembled jar. This is what the final ``build.sbt`` should contain: |
| 26 | + |
| 27 | +.. code-block:: scala |
| 28 | +
|
| 29 | + // the assembly settings |
| 30 | + assemblySettings |
| 31 | +
|
| 32 | + // we specify the name for our fat jar |
| 33 | + jarName in assembly := "assembly-project.jar" |
| 34 | +
|
| 35 | + // using the java server for this application. java_application is fine, too |
| 36 | + packageArchetype.java_server |
| 37 | +
|
| 38 | + // removes all jar mappings in universal and appends the fat jar |
| 39 | + mappings in Universal := { |
| 40 | + // universalMappings: Seq[(File,String)] |
| 41 | + val universalMappings = (mappings in Universal).value |
| 42 | + val fatJar = (assembly in Compile).value |
| 43 | + // removing means filtering |
| 44 | + val filtered = universalMappings filter { |
| 45 | + case (file, name) => ! name.endsWith(".jar") |
| 46 | + } |
| 47 | + // add the fat jar |
| 48 | + filtered :+ (fatJar -> ("lib/" + fatJar.getName)) |
| 49 | + } |
| 50 | + |
| 51 | +
|
| 52 | + // the bash scripts classpath only needs the fat jar |
| 53 | + scriptClasspath := Seq( (jarName in assembly).value ) |
| 54 | + |
| 55 | +Multi Module Builds |
| 56 | +------------------- |
| 57 | + |
| 58 | + **Main Goal** |
| 59 | + |
| 60 | + | Aggregate multiple projects into one native package |
| 61 | +
|
| 62 | +If you want to aggregate different projects in a multi module build to a single package, |
| 63 | +you can specify everthing in a single ``build.sbt`` |
| 64 | + |
| 65 | +.. code-block:: scala |
| 66 | +
|
| 67 | + import NativePackagerKeys._ |
| 68 | +
|
| 69 | + name := "mukis-fullstack" |
| 70 | +
|
| 71 | + // used like the groupId in maven |
| 72 | + organization in ThisBuild := "de.mukis" |
| 73 | +
|
| 74 | + // all sub projects have the same version |
| 75 | + version in ThisBuild := "1.0" |
| 76 | +
|
| 77 | + scalaVersion in ThisBuild := "2.11.2" |
| 78 | +
|
| 79 | + // common dependencies |
| 80 | + libraryDependencies in ThisBuild ++= Seq( |
| 81 | + "com.typesafe" % "config" % "1.2.0" |
| 82 | + ) |
| 83 | +
|
| 84 | + // this is the root project, aggregating all sub projects |
| 85 | + lazy val root = Project( |
| 86 | + id = "root", |
| 87 | + base = file("."), |
| 88 | + // configure your native packaging settings here |
| 89 | + settings = packageArchetype.java_server++ Seq( |
| 90 | + maintainer := "John Smith <[email protected]>", |
| 91 | + packageDescription := "Fullstack Application", |
| 92 | + packageSummary := "Fullstack Application", |
| 93 | + // entrypoint |
| 94 | + mainClass in Compile := Some("de.mukis.frontend.ProductionServer") |
| 95 | + ), |
| 96 | + // always run all commands on each sub project |
| 97 | + aggregate = Seq(frontend, backend, api) |
| 98 | + ) dependsOn(frontend, backend, api) // this does the actual aggregation |
| 99 | +
|
| 100 | + // --------- Project Frontend ------------------ |
| 101 | + lazy val frontend = Project( |
| 102 | + id = "frontend", |
| 103 | + base = file("frontend") |
| 104 | + ) dependsOn(api) |
| 105 | +
|
| 106 | +
|
| 107 | + // --------- Project Backend ---------------- |
| 108 | + lazy val backend = Project( |
| 109 | + id = "backend", |
| 110 | + base = file("backend") |
| 111 | + ) dependsOn(api) |
| 112 | +
|
| 113 | + // --------- Project API ------------------ |
| 114 | + lazy val api = Project( |
| 115 | + id = "api", |
| 116 | + base = file("api") |
| 117 | + ) |
| 118 | + |
| 119 | + |
| 120 | +Custom Packaging Format |
| 121 | +----------------------- |
| 122 | + |
| 123 | + **Main Goal** |
| 124 | + |
| 125 | + | Use native packager to define your own custom packaging format |
| 126 | + | and reuse stuff you already like |
| 127 | +
|
| 128 | +The very core principle of native packager are the ``mappings``. They are a sequence |
| 129 | +of ``File -> String`` tuples, that map a file on your system to a location on your install |
| 130 | +location. |
| 131 | + |
| 132 | +Defining a custom mapping format is basically transforming these mappings into the format |
| 133 | +of you choice. To do so, we recommend the following steps |
| 134 | + |
| 135 | +1. Create a new configuration ``scope`` for you packaging type |
| 136 | +2. Define a ``packageBin`` task in your new scope that transforms the mappings into a package |
| 137 | + |
| 138 | +The following examples demonstrates how to create a simple *text format*, which lists all your |
| 139 | +mappings inside a package format. A minimal ``build.sbt`` would look like this |
| 140 | + |
| 141 | +.. code-block:: scala |
| 142 | +
|
| 143 | + import NativePackagerKeys._ |
| 144 | +
|
| 145 | + val TxtFormat = config("txtFormat") |
| 146 | +
|
| 147 | + val root = project.in(file(".")) |
| 148 | + // adding your custom configuration scope |
| 149 | + .configs( TxtFormat ) |
| 150 | + .settings(packageArchetype.java_server:_*) |
| 151 | + .settings( |
| 152 | + name := "mukis-custom-package", |
| 153 | + version := "1.0", |
| 154 | + mainClass in Compile := Some("de.mukis.ConfigApp"), |
| 155 | + maintainer in Linux := "Nepomuk Seiler <[email protected]>", |
| 156 | + packageSummary in Linux := "Custom application configuration", |
| 157 | + packageDescription := "Custom application configuration", |
| 158 | + // defining your custom configuration |
| 159 | + packageBin in TxtFormat := { |
| 160 | + val fileMappings = (mappings in Universal).value |
| 161 | + val output = target.value / s"${packageName.value}.txt" |
| 162 | + // create the is with the mappings. Note this is not the ISO format -.- |
| 163 | + IO.write(output, "# Filemappings\n") |
| 164 | + // append all mappings to the list |
| 165 | + fileMappings foreach { |
| 166 | + case (file, name) => IO.append(output, s"${file.getAbsolutePath}\t$name${IO.Newline}") |
| 167 | + } |
| 168 | + output |
| 169 | + } |
| 170 | + ) |
| 171 | +
|
| 172 | +To create your new "packageFormat" just run |
| 173 | + |
| 174 | +.. code-block:: bash |
| 175 | +
|
| 176 | + txtFormat:packageBin |
| 177 | + |
| 178 | +If you want to read more about sbt configurations: |
| 179 | + |
| 180 | +* `sbt tasks <http://www.scala-sbt.org/0.13/docs/Tasks.html>`_ |
| 181 | +* `sbt configurations <http://www.scala-sbt.org/0.13.5/docs/Detailed-Topics/Testing.html#additional-test-configurations-with-shared-sources>`_ |
| 182 | +* `custom configuration <http://stackoverflow.com/questions/18789477/define-custom-configuration-in-sbt>`_ |
| 183 | + |
0 commit comments