I'm trying to enclose some legacy code in a maven plugin. The legacy code is writing its outputs in the OS current working directory. I want to move all that into the target dir. I.e. if my project nesting is
A
-B
-C
and I run mvn install in B or C, I get the outputs in B or C and want to move them to B/target and C/target. Even worse, if I launch mvn install in A, outputs for both B and C wind up in A, which is obviously very bad (overwrites).
I've looked for a way to get the current project folder (not the place mvn install was launched from), but have found nothing so far that doesn't involve pushing this all back at the user (command line hacks, pom hacks, properties files, etc). I've also wished for a way to change the working directory before launching the legacy, but Java doesn't allow that. So how should I proceed?
Just a quick note: for latest maven plugins, annotations could be used instead:
#Parameter(readonly = true, defaultValue = "${project}")
private MavenProject project;
To answer my own question yet again (happens to me a lot), the solution starts here:
/**
* The maven project.
*
* #parameter expression="${project}"
* #readonly
*/
private MavenProject project;
Then in a setup method:
this.model = project.getModel();
this.build = model.getBuild();
this.finalName = build.getFinalName();
this.targetDir = new File(build.getDirectory());
which gets targetDir pointing at the right place. Then the legacy code runs, dropping dots wherever it pleases and the plugin cleans up by MOVING them into target dir.
That was good enough for starters. But with targetDir available, I actually wound up toilet training the legacy code to do its business in the right place.
If you only want to get the directory where the artifact is built it might be a bit neater to do this:
/**
* #parameter expression="${project.build.directory}/${project.build.finalName}"
* #readonly
*/
private File outputPath;
Related
I have a project written in C. Currently it's using UNIX makefiles to compile itself for Linux, but recently I've been looking into CMake, to be more portable.
The executable, when running, needs to access some asset files that are part of the project. When using makefiles, I would just compile the project with:
make prefix=/usr
make prefix=/usr install
So while the project is compiled, it knows that it will end up in /usr, and when running, it searching for its own project files there (in something like /usr/share/my-project/).
I created a very basic CMakeLists.txt, that compiles the .exe file, and installs it together with one other asset file in the install directory. I then run the following commands to create an NSIS installer for Windows:
cmake.exe --build --config Release .
cpack.exe
Which succesfully gives me the NSIS installer. When run, it shows the user a few steps, one of them is to decide where the project will be installed, which the user can modify.
So my question is, at that point the project has already been compiled, so how can I pass to my project its own install location, so it can access files included in the project? How do other projects do this? I couldn't find much information online about it, which makes me think I might be taking the wrong approach.
For anyone stuck in a similar problem, I found one solution.
Upon looking online, this seems to be something not recommended for Unix systems, and setting the install location during compilation is pretty standard.
For windows, however, I found the function GetModuleFileNameW (GetModuleFileNameW function (libloaderapi.h)).
It returns the path to the current executable (something like C:\Program Files\<my-app>\bin\my-app.exe). I've confirmed it returns the right path, even when I install the project on different directories. It returns the result using wchar_t, so unicode directories are also supported.
Here is a small example of how it can be used:
// to keep the example simple, this is assuming maximum 1000 characters in the path
wchar_t dynamicProjectLocationW[1000];
GetModuleFileNameW(NULL, dynamicProjectLocationW, 999);
dynamicProjectLocationW[999] = L'\0';
// given a path like "C:\X\Y\bin\myapp.exe" find the second to last slash
// so we can get the path "C:\X\Y\"
wchar_t *pointer = dynamicProjectLocationW;
wchar_t *secondToLastSlash = 0;
wchar_t *lastSlash = 0;
while (pointer[0] != L'\0') {
if (pointer[0] == L'\\') {
secondToLastSlash = lastSlash;
lastSlash = pointer;
}
pointer++;
}
// cut the path short, so we can use the project path to find other files
if (secondToLastSlash) {
secondToLastSlash++;
secondToLastSlash[0] = L'\0';
}
This is solving my problem for now, so I'll be using this until a better solution is found.
I am new to Meson so please forgive me if this is a stupid question.
Simple Version of the Question:
I want to be able to assign a dynamic version number to the meson project version at build time. Essentially meson.project_version()=my_dynamic_var or project( 'my_cool_project', 'c', version : my_dynamic_var') (which of course won't work).
I would rather not pre-process the file if I don't have to.
Some background if anybody cares:
My build system dynamically comes up with a version number for the project. In my case, it is using a bash script. I have no problem getting that version into my top level meson.build file using run_command and scraping stdout from there. I have read that using doing it this way is bad form so if there is another way to do this.. I am all ears.
I am also able to create and pass the correct -DPRODUCT_VERSION="<my_dynamic_var>" via add_global_arguments so I COULD just settle for that.. but I would like the meson project itself to carry the same version for the logs and so I can use meson.project_version() to get the version in subprojects for languages other than c/c++.
The short answer, as noted in comments to the question, appears to be no. There is no direct way to set the version dynamically in the project call.
However, there are some work arounds, and the first looks promising for the simple case:
(1) use meson rewriting capability
$ meson rewrite kwargs set project / version 1.0.0
Then obviously use an environment variable instead of 1.0.0.
(2) write a wrapper script which reads the version from the environment and substitutes it into your meson.build file in the project call.
(3) adopt conan.io and have your meson files generated.
(4) use build options. This option, while not as good as (1) might work for other work flows.
Here's how option (4) works.
create a meson_options.txt file in your meson root directory
add the following line:
option('version', type : 'string', value : '0.0.0', description : 'project version')
then create a meson.build file that reads this option.
project('my_proj', 'cpp')
version = get_option('version')
message(version)
conf_data = configuration_data()
conf_data.set('version', version)
When you go to generate your project, you have an extra step of setting options.
$ meson build && cd build
$ meson configure -Dversion=$BUILD_VERSION
Now the version is available as a build option, then we use a configuration_data object to make it available for substitution into header/source files (which you might want to get it into shared libraries or what not).
configure_file(
input : 'config.hpp.in',
output : 'config.hpp',
configuration : conf_data
)
And config.hpp.in looks something like this:
#pragma once
#include <string>
const static std::string VERSION = "#version#";
When we do the configure_file call, #version# will get substituted for the version string we set in the meson configure step.
So this way is pretty convoluted, but like I said, you may still end up doing some of it, e.g. to print copyright info and what not.
As of 0.60.3 you may directly assign version from run_command which means the following will work without any meson_options.txt.
project('randomName', 'cpp',
version : run_command('git', 'rev-parse', '--short', 'HEAD').stdout().strip(),
default_options : [])
In particular, it is also possible to assign the result of a bash script, simply invoke it instead of git.
Short:
How do I use header only libraries with biicode?
Medium:
When I try to build a block it includes example directories even though I try to set the dependencies explicitly in the biicode.conf of the published block.
Long:
I'm trying to get the unity framework up and running, using biicode.
Unity is great as a unit testing framework for C because you do not need to compile any libraries. If you do your own mocks, you don't even have to run any scripts - there is just a single .c file to include in your compile and you are golden.
I've published the git repo to my biicode block paulbendixen/Unity and since there is no need for any compilation step beyond the c file that accompanies the header that should be included there is nothing else to do.
However, when I include the file, using #include "paulbendixen/Unity/src/unity.h" I get the error when doing bii cpp:build:
Code.c:2:28: fatal error: ProductionCode.h: No such file or directory
#include "ProductionCode.h"
This is in the examples folder and should therefore not be compiled, when I just want to use the unit testing part. Changing the [dependencies] to include unity.h = unity.c unity_internals.h hasn't helped either.
I'm pretty sure the problem should be resolved in the Unity/biicode.conf, but I haven't been able to find a thorough description of this file anywhere.
The simplicity of the Unity library should make it ideal for a build system such as bii, but it seems quite complex to set up.
If it helps, I've used the simple layout and the -r [github for throwtheswitch] option
It is not that simple. Unity uses Rakefiles to build and run the tests, and they have lots of configuration. What can be done for quickly upload it to biicode is just to ignore the tests and publish just the files. This can be done writing a ignore.bii file with the contents:
docs/*
test/*
examples/*
*test*
Wrt to the biicode.conf, the only configuration necessary are the include paths:
[paths]
src
extras/fixture/src
You can check that the manual definition of dependencies is not necessary, if you run $ bii deps --files *unity.h
With these changes, it is possible to publish it. Nothing to build.
Then, to use it in other projects, I have been able to build simple tests:
#include "unity.h"
void testTrue(void){
TEST_ASSERT(1);
TEST_ASSERT_TRUE(1);
}
int main() {
testTrue();
}
Just adding the following to the biicode.conf of the new project:
[requirements]
diego/unityfork: 0
[includes]
unity.h: diego/unityfork/src
It would probably be much easier to make biicode run and build the tests without ignoring them if it used the more typical CMake configuration instead of Rakefiles
I have a C program built using Autotools. In src/Makefile.am, I define a macro with the path to installed data files:
AM_CPPFLAGS = -DAM_INSTALLDIR='"$(pkgdatadir)"'
The problem is that I need to run make install before I can test the binary (since it needs to be able to find the data files).
I can define another macro with the path of the source tree so the data files can be located without installing:
AM_CPPFLAGS = -DAM_INSTALLDIR='"$(pkgdatadir)"' -DAM_TOPDIR='"$(abs_top_srcdir)"'
Now, I would like the following behavior:
If the binary was installed via make install, use AM_INSTALLDIR to fetch data files.
If the binary was not installed, use AM_TOPDIR to fetch data files.
Is this possible? Is there a better approach to this problem?
What I do (in https://http://rhdunn.github.com/cainteoir/) is:
const char *basedir = getenv("CAINTEOIR_DATADIR");
if (!basedir)
basedir = DATADIR "/" PACKAGE; // e.g. /usr/share/cainteoir-engine
and then run it (in tests/harness.py) as:
CAINTEOIR_DATADIR=`pwd`/data src/apps/metadata/metadata test_file.epub
This then allows the user to change the location of where to get the data if they wish.
Making the program able to use a run-time configuration as proposed by reece is a good solution. If for some reason you do not want it to be configurable at run-time, a common solution is to build a test binary differently than the installed binary (there are other problems associated with this, in particular ensuring that the program you are testing has behavior that is consistent with the program that is installed.) An easy way to do that is something like:
bin_PROGRAMS = foo
check_PROGRAMS = test-foo
test_foo_SOURCES = $(foo_SOURCES)
AM_CPPFLAGS = -DINSTALLDIR='"$(pkgdatadir)"'
test_foo_CPPFLAGS = -DINSTALLDIR='"$(abs_top_srcdir)"'
Rather than using a binary with a different name, you might want to have a dedicated tests directory and build the program using the same name as the original.
Note that I've changed the name from AM_INSTALLDIR to INSTALLDIR. Automake reserves names
beginning with "AM_" for its own use, and by using that name you are stomping on Automake's
namespace.
A bit of additional information first: The data files are under active development, and I have various scripts that need to call binaries using local data files, whereas installed binaries should use stable, installed data files.
My original solution made use of an environment variable, as proposed by reece. But I didn't want to manage setting up environment variables in various places, and I didn't want any risk of the wrong data files being picked up due to a mistake.
So the solution I ended up with was to define macros for both locations at build time, and add a flag (-local) to the binaries to force local data files to be used.
I want to build a shared library using waf as it looks much easier and less cluttered than GNU autotools.
I actually have several questions so far related to the wscript I've started to write:
VERSION='0.0.1'
APPNAME='libmylib'
srcdir = '.'
blddir = 'build'
def set_options(opt):
opt.tool_options('compiler_cc')
pass
def configure(conf):
conf.check_tool('compiler_cc')
conf.env.append_value('CCFLAGS', '-std=gnu99 -Wall -pedantic -ggdb')
def build(bld):
bld.new_task_gen(
features = 'cc cshlib',
source = '*.c',
target='libmylib')
The line containing source = '*.c' does not work. Must I specify each and every .c file instead of using a wildcard?
How can I enable a debug build for example (currently the wscript is using the debug builds CFLAGS, but I want to make this optional for the end user).
It is planned for the library sources to be within a sub directory, and programs that use the lib each in their own sub directories.
Assuming you are using the latest version of waf (1.5.9 at the time of writing), wild cards can be specified via the glob() method on the build context. So you can write the following:
bld.new_task_gen(
features = 'cc cshlib',
source = bld.glob('*.c'),
target='mylib')
If you were using an older version of waf that doesn't have glob, then there is a method find_sources_in_dirs that you can use:
lib = bld.new_task_gen(
features = 'cc cshlib',
target = 'mylib')
lib.find_sources_in_dirs('.')
This method is still in Waf but is slated for deprecation and may eventually disappear.
The srcdir and blddir variables are optional now so you don't need them - they default to "." and "build" anyway. You shouldn't prepend "lib" to the target name, this is done automatically in a platform specific way (on Windows no lib is added and shared libraries use .dll). Debug vs Release build is a surprisingly thorny issue. Originally Waf included this feature, but it was dropped at some point and never re-added. It's a common request on the mailing list so may resurface in the future. Meanwhile you could do a lot worse than use gjc's cflags module. Just add it to your project directory. The final wscript would then be:
VERSION='0.0.1'
APPNAME='mylib'
def set_options(opt):
opt.tool_options('compiler_cc')
opt.tool_options('cflags', tooldir='.')
def configure(conf):
conf.check_tool('compiler_cc')
conf.check_tool('cflags', tooldir='.')
def build(bld):
bld.new_task_gen(
features = 'cc cshlib',
source = bld.glob('*.c'),
target=APPNAME)
And to set up a debug build you would run the following:
./waf configure -d debug
If you are using libraries in their own sub-directories, then you should probably have a top level wscript and use the bld.add_subdirs() technique to add library/program directories. Each sub-directory would have its own wscript_build file. You can then use the export_incdirs and uselib_local properties to specify the correct include directories between library and program "modules".
waf has changed a lot over the years so neither the code in the question nor the answer works with current waf anymore. Nowadays you just write:
def options(ctx):
ctx.load('compiler_c')
def configure(ctx):
ctx.load('compiler_c')
def build(ctx):
ctx.shlib(source = ctx.path.ant_glob('src/*.c'),
target = 'name')
Note that waf will automatically add the lib prefix so you don't write target
= 'libname'. On Windows, you also have to add the defs keyword argument to the shlib function call.
Personally I would recommend against recursive build scripts involving wscript_build files. Not that it doesn't work (like recursive makefiles), it's just much simpler to keep all logic in one medium-sized build script.