Recently I've changed from CMake to Premake (v5.0.0-alpha8) and I'm not quite sure how to achieve the the following in Premake.
I want to include some dependencies so in CMake I can do something like this:
target_link_libraries(${PROJECT_NAME}
${YALLA_ABS_PLATFORM}
${YALLA_LIBRARY})
The above will add the paths of these libraries (dir) to "Additional Include Directories" in the compiler and it will also add an entry (lib) to "Additional Dependencies" in the linker so I don't need to do anything special beyond calling target_link_libraries.
So I expected that when I'm doing something like this in Premake:
links {
YALLA_LIBRARY
}
I'd get the same result but I don't.
I also tried to use the libdirs but it doesn't really work, I mean I can't see the library directory and its subdirectories passed to the compiler as "Additional Include Directories" (/I) or Yalla.Library.lib passed to the the linker as "Additional Dependencies".
Here is the directory structure I use:
.
|-- src
| |-- launcher
| |-- library
| | `-- utils
| `-- platform
| |-- abstract
| `-- win32
`-- tests
`-- platform
`-- win32
The library dir is defined in Premake as follow:
project(YALLA_LIBRARY)
kind "SharedLib"
files {
"utils/string-converter.hpp",
"utils/string-converter.cpp",
"defines.hpp"
}
The platform dir is defined in Premake as follow:
project(YALLA_PLATFORM)
kind "SharedLib"
includedirs "abstract"
links {
YALLA_LIBRARY
}
if os.get() == "windows" then
include "win32"
else
return -- OS NOT SUPPORTED
end
The win32 dir is defined in Premake as follow:
files {
"event-loop.cpp",
"win32-exception.cpp",
"win32-exception.hpp",
"win32-window.cpp",
"win32-window.hpp",
"window.cpp"
}
And finally at the root dir I have the following Premake file:
PROJECT_NAME = "Yalla"
-- Sets global constants that represents the projects' names
YALLA_LAUNCHER = PROJECT_NAME .. ".Launcher"
YALLA_LIBRARY = PROJECT_NAME .. ".Library"
YALLA_ABS_PLATFORM = PROJECT_NAME .. ".AbstractPlatform"
YALLA_PLATFORM = PROJECT_NAME .. ".Platform"
workspace(PROJECT_NAME)
configurations { "Release", "Debug" }
flags { "Unicode" }
startproject ( YALLA_LAUNCHER )
location ( "../lua_build" )
include "src/launcher"
include "src/library"
include "src/platform"
I'm probably misunderstanding how Premake works due to lack of experience with it.
I solved it by creating a new global function and named it includedeps.
function includedeps(workspace, ...)
local workspace = premake.global.getWorkspace(workspace)
local args = { ... }
local args_count = select("#", ...)
local func = select(args_count, ...)
if type(func) == "function" then
args_count = args_count - 1
args = table.remove(args, args_count)
else
func = nil
end
for i = 1, args_count do
local projectName = select(i, ...)
local project = premake.workspace.findproject(workspace, projectName)
if project then
local topIncludeDir, dirs = path.getdirectory(project.script)
if func then
dirs = func(topIncludeDir)
else
dirs = os.matchdirs(topIncludeDir .. "/**")
table.insert(dirs, topIncludeDir)
end
includedirs(dirs)
if premake.project.iscpp(project) then
libdirs(dirs)
end
links(args)
else
error(string.format("project '%s' does not exist.", projectName), 3)
end
end
end
Usage:
includedeps(PROJECT_NAME, YALLA_LIBRARY)
or
includedeps(PROJECT_NAME, YALLA_PLATFORM, function(topIncludeDir)
return { path.join(topIncludeDir, "win32") }
end)
Update:
For this to work properly you need to make sure that when you include the dependencies they are included by their dependency order and not by the order of the directory structure.
So for example if I have the following dependency graph launcher --> platform --> library then I'll have to include them in the following order.
include "src/library"
include "src/platform"
include "src/launcher"
As opposed to the directory structure that in my case is as follow:
src/launcher
src/library
src/platform
If you will include them by their directory structure it will fail and tell you that "The project 'Yalla.Platform' does not exist."
Related
I'm trying to experiment with a local copy of the akka-http library. I can publish it locally with sbt publishLocal, but I can't figure out how to change the version number. build.sbt contains an organization field but no simple version field - that seems to be generated from somewhere else and I can't figure out where. It's currently at 10.0.5but grepping that string in the source doesn't turn up anything obvious.
Seems like a simple question, but where is version defined? Thanks.
(I'm asking this because sbt docs tell me I should name my local version something like 0.1-SNAPSHOT. I assume there must be a simpler way to do this than by disabling the auto-generation logic and hardcoding it into build.sbt)
It seems that Akka-HTTP generates its version at run-time.
If you look at akka-http/akka-http-core/src/main/resources/reference.conf:
Akka HTTP version, checked against the runtime version of Akka HTTP.
Loaded from generated conf file.
And then look at akka-http/project/Version.scala:
/**
* Generate version.conf and akka/Version.scala files based on the version setting.
*/
object Version {
def versionSettings: Seq[Setting[_]] = inConfig(Compile)(Seq(
resourceGenerators += generateVersion(resourceManaged, _ / "akka-http-version.conf",
"""|akka.http.version = "%s"
|""").taskValue,
sourceGenerators += generateVersion(sourceManaged, _ / "akka" / "http" / "Version.scala",
"""|package akka.http
|
|import com.typesafe.config.Config
|
|object Version {
| val current: String = "%s"
| def check(config: Config): Unit = {
| val configVersion = config.getString("akka.http.version")
| if (configVersion != current) {
| throw new akka.ConfigurationException(
| "Akka JAR version [" + current + "] does not match the provided " +
| "config version [" + configVersion + "]")
| }
| }
|}
|""").taskValue
))
def generateVersion(dir: SettingKey[File], locate: File => File, template: String) = Def.task[Seq[File]] {
val file = locate(dir.value)
val content = template.stripMargin.format(version.value)
if (!file.exists || IO.read(file) != content) IO.write(file, content)
Seq(file)
}
}
I'm assuming after generating the current version you should see a akka-http-version.conf file somewhere in your filesystem.
I'm building a Docker image with a fat jar. I use the sbt-assembly plugin to build the jar, and the sbt-native-packager to build the Docker image. I'm not very familiar with SBT and am running into the following issues.
I'd like to declare a dependency on the assembly task from the docker:publish task, such that the fat jar is created before it's added to the image. I did as instructed in the doc, but it's not working. assembly doesn't run until I invoke it.
publish := (publish dependsOn assembly).value
One of the steps in building the image is copying the fat jar. Since assembly plugin creates the jar in target/scala_whatever/projectname-assembly-X.X.X.jar, I need to know the exact scala_whatever and the jar name. assembly seems to have a key assemblyJarName but I'm not sure how to access it. I tried the following which fails.
Cmd("COPY", "target/scala*/*.jar /app.jar")
Help!
Answering my own questions, the following works:
enablePlugins(JavaAppPackaging, DockerPlugin)
assemblyMergeStrategy in assembly := {
case x => {
val oldStrategy = (assemblyMergeStrategy in assembly).value
val strategy = oldStrategy(x)
if (strategy == MergeStrategy.deduplicate)
MergeStrategy.first
else strategy
}
}
// Remove all jar mappings in universal and append the fat jar
mappings in Universal := {
val universalMappings = (mappings in Universal).value
val fatJar = (assembly in Compile).value
val filtered = universalMappings.filter {
case (file, name) => !name.endsWith(".jar")
}
filtered :+ (fatJar -> ("lib/" + fatJar.getName))
}
dockerRepository := Some("username")
import com.typesafe.sbt.packager.docker.{Cmd, ExecCmd}
dockerCommands := Seq(
Cmd("FROM", "username/spark:2.1.0"),
Cmd("WORKDIR", "/"),
Cmd("COPY", "opt/docker/lib/*.jar", "/app.jar"),
ExecCmd("ENTRYPOINT", "/opt/spark/bin/spark-submit", "/app.jar")
)
I completely overwrite the docker commands because the defaults add couple of scripts that I don't need because I overwrite the entrypoint as well. Also, the default workdir is /opt/docker which is not where I want to put the fat jar.
Note that the default commands are shown by show dockerCommands in sbt console.
I'm using a qtcreator 4.0.3 and I try to create a directory struct to
all builds, like this:
~/Build/project01
~/Build/project02
...
~/Build/projectNN
I'm using this .pro file:
:><------- Cutting ------------
#
# BUILDDIR Where the executable file was create
#
BUILDDIR = $$HOME_PATH/Builds/$$APP_NAME
#
# All Directory of temporary files
#
OBJECTS_DIR = $$BUILDDIR/obj
MOC_DIR = $$BUILDDIR/moc
RCC_DIR = $$BUILDDIR/rcc
UI_DIR = $$BUILDDIR/ui
DESTDIR = $$BUILDDIR/bin
#
# If the dirs not exists, I'll create it
#
THis instructions are ignored
Any directory was created.
!exists( $$DESTDIR ) {
message("Creating $$DESTDIR struct")
mkpath( $$DESTDIR )
}
!exists( $$OBJECTS_DIR ) {
message("Creating $$OBJECTS_DIR")
mkpath( $$OBJECTS_DIR )
}
!exists( $$MOC_DIR ) {
message("Creating $$MOC_DIR")
mkpath( $$MOC_DIR )
}
!exists( $$RCC_DIR ) {
message("Creating $$RCC_DIR")
mkpath( $$RCC_DIR )
}
!exists( $$UI_DIR ) {
message("Creating $$UI_DIR")
mkpath( $$UI_DIR )
}
:><------- Cutting ------------
But this not work. Always qtcreator use a default directory struct
configured in Tools->Options->Build and Run->Default build Directory.
Simple it's ignore this instructions ...
Am I doing something wrong??
This is an anti-pattern, and it generally won't work because no one using qmake projects expects them to behave that way: Qt Creator doesn't, other people don't, etc.
For any given project, you set the build directory by executing qmake in the build directory. E.g., supposed you have ~/src/myproject/myproject.pro that you wish to build:
mkdir -p ~/build/myproject
cd ~/build/myproject
qmake ~/src/myproject
gmake -j8
To build multiple projects in subfolders, you should have a top-level subdirs project that refers to them. E.g.
+--- myproject
+--- myproject.pro
+--- lib1
| +--- lib1.pro
| ...
+--- lib2
| +--- lib2.pro
| ...
+--- app1
. +--- app1.pro
. ...
etc.
The source directory structure will be replicated in the build folder. You generally shouldn't otherwise care what goes where in the build folder, except when referencing to targets built by subprojects, e.g. if app1 depends on lib2 you will need to add the build product as a dependency. This answer shows exactly how to do that.
Because of the large size of some resource files, I'd like sbt package to create 2 jar files at the same time, e.g. project-0.0.1.jar for the classes and project-0.0.1-res.jar for the resources.
Is this doable?
[SOLUTION] based on the answer below thanks to #gilad-hoch
1) unmanagedResources in Compile := Seq()
Now it's just classes in the default jar.
2)
val packageRes = taskKey[File]("Produces a jar containing only the resources folder")
packageRes := {
val jarFile = new File("target/scala-2.10/" + name.value + "_" + "2.10" + "-" + version.value + "-res.jar")
sbt.IO.jar(files2TupleRec("", file("src/main/resources")), jarFile, new java.util.jar.Manifest)
jarFile
}
def files2TupleRec(pathPrefix: String, dir: File): Seq[Tuple2[File, String]] = {
sbt.IO.listFiles(dir) flatMap {
f => {
if (f.isFile) Seq((f, s"${pathPrefix}${f.getName}"))
else files2TupleRec(s"${pathPrefix}${f.getName}/", f)
}
}
}
(packageBin in Compile) <<= (packageBin in Compile) dependsOn (packageRes)
Now when I do "sbt package", both the default jar and a resource jar are produced at the same time.
to not include the resources in the main jar, you could simply add the following line:
unmanagedResources in Compile := Seq()
to add another jar, you could define a new task. it would generally be something like that:
use sbt.IO jar method to create the jar.
you could use something like:
def files2TupleRec(pathPrefix: String, dir: File): Seq[Tuple2[File,String]] = {
sbt.IO.listFiles(dir) flatMap {
f => {
if(f.isFile) Seq((f,s"${pathPrefix}${f.getName}"))
else files2TupleRec(s"${pathPrefix}${f.getName}/",f)
}
}
}
files2TupleRec("",file("path/to/resources/dir")) //usually src/main/resources
or use the built-in methods from Path to create the sources: Traversable[(File, String)] required by the jar method.
that's basically the whole deal...
I'm trying to define a jar task for all sub projects (about 30). I tried the following task:
jar {
destinationDir = file('../../../../_temp/core/modules')
archiveName = baseName + '.' + extension
metaInf {
from 'ejbModule/META-INF/' exclude 'MANIFEST.MF'
}
def manifestClasspath = configurations.runtime.collect { it.getName() }.join(',')
manifest {
attributes("Manifest-Version" : "1.0",
"Created-By" : vendor,
"Specification-Title" : appName,
"Specification-Version" : version,
"Specification-Vendor" : vendor,
"Implementation-Title" : appName,
"Implementation-Version" : version,
"Implementation-Vendor" : vendor,
"Main-Class" : "com.dcx.epep.Start",
"Class-Path" : manifestClasspath
)
}
}
My problem is, that the dependencies between the sub projects are not included in the manifest's classpath. I tried changing the runtime configuration to a compile configuration but that results in the following error.
What went wrong: A problem occurred evaluating project ':EskoordClient'.
You can't change a configuration which is not in unresolved state!
That is my complete build file for project EskoordClient:
dependencies {
compile project(':ePEPClient')
}
Most of my sub projects build files only define the projects dependencies. 3rd party lib dependencies are defined in the build file of the super project.
Is there a possibility to include all needed classpath entries (3rd party libraries and other projects) to a manifest classpath in a superproject for all subprojects.
This is how I got it to work. Get Project dependencies only using the call:
getAllDependencies().withType(ProjectDependency)
then adding the contents of each project's libsDir to my Class-Path manifest entry.
jar {
manifest {
attributes 'Main-Class': 'com.my.package.Main'
def manifestCp = configurations.runtime.files.collect {
File file = it
"lib/${file.name}"
}.join(' ')
configurations.runtime.getAllDependencies().withType(ProjectDependency).each {dep->
def depProj = dep.getDependencyProject()
def libFilePaths = project(depProj.path).libsDir.list().collect{ inFile-> "lib/${inFile}" }.join(' ')
logger.info"Adding libs from project ${depProj.name}: [- ${libFilePaths} -]"
manifestCp += ' '+libFilePaths
}
logger.lifecycle("")
logger.lifecycle("---Manifest-Class-Path: ${manifestCp}")
attributes 'Class-Path': manifestCp
}
}