I've inherited an Ada/C++ project and I'm trying to use gprbuild to automate the build process (which was previously done with a set of about 12 .bat files). I'm totally new to Ada and gprbuild, but have actually made pretty good progress. I can compile the .exe's that I need, but not the library. I am not at liberty to completely share the .gpr file, but the relevant parts look like this:
[snip]
for Source_Dirs use (
"c_plus_plus_files",
"ada_files",
"..\another_project\some_other_ada_files",
"..\another_project\even_more_ada_files"
);
[snip]
for Source_Files use (
"my_ada_file.ads",
"another_ada_file.ads",
"one_more_ada_file.adb",
"c_plus_plus_file.cpp"
);
[snip]
When I run "gprbuild -P my_project.gpr" it in turn runs "gcc -c gnat5 one_more_ada_file.adb" and complains that it cannot find a certain file that one_more_ada_file.adb depends on. The dependency is in ..\another_project\even_more_ada_files, so I would expect it to be found. But if I copy the dependency into the same folder as one_more_ada_file.adb, the error goes away.
Because of how the VCS is setup and how we're sharing code between two projects, I'd much rather figure out what's wrong with how I'm using "for source_dirs use" than to keep multiple copies of all the ada files.
Again, I'm an Ada/GPS newb, so if I'm leaving out relevant information, please let me know.
Update: It appears that the specific problem isn't that source_dirs isn't doing anything at all, but that it doesn't handle having two source dirs where .ads files in one dir depend on .ads files in the other. That is, even within my "other" project above, an .ads file in some_other_ada_files that depends on an .ads file in even_more_ada_files doesn't get compiled with the gcc -c -gnat05 command when I run gprbuild (error: the file in even_more_ada_files not found), but it does get compiled if I run the gcc command by hand (or in a .bat script) with two -I flags, one for each directory.
When dealing with multiple projects, you should normally create a .gpr-file for each project, and let your projects depend on the other projects as needed.
Thus:
project another_project is
for Source_Dirs use
("some_other_ada_files",
"even_more_ada_files");
end another_project;
and then:
with "..\another_project\another_project.gpr"
project The_Project is
for Source_Dirs use
("c_plus_plus_files",
"ada_files");
end The_Project;
Related
To learn the processes behind compilation of Ada programs (without resorting to using gpr), I've decided to compile one of my projects manually. To compile a program, I run the following command on each .adb file therein (not manually, however):
$ gcc -c src/<file>.adb -o obj/<file>.o
That compiles all the files and puts them in the obj directory.
Or rather, it would. There is a slight problem with this. I have an archive (static library) that I've generated from another project, called libapples.a, containing package Apples. The Apples package is used by the files you see me compiling above.
Since libapples.a doesn't have source files anymore (given its archive format), it's not viable (not even possible) to provide the sources of that library to the command above using -I switches; on the other hand, if I don't have the sources included, the command above comes back to me saying:
<file>.adb:<y>:<x>: file "apples.ads" not found
gnatmake: "src/<file>.adb" compilation error
I've attempted to include the library in the compilation process by using flags -L and l (which I believe is how you'd do it in C; feel free to correct me if I'm wrong). In another attempt I placed the archive right in the source directory.
How does one go about including a library in the compilation process?
apples.ads is somewhat like a header in C and you definitely need it for your file to compile. You should use -I to point gcc to the directory where apples.ads is located in.
Be aware that compiling an Ada source not only yields an object file, but also an Ada Library Information (.ali) file. You'll need that for the next step. You should not use the -o flag because it may lead to your .o and .ali files having different names.
On the other hand, since you only want to produce an object file, you should use -c to tell gcc to only compile. In other languages like C, gcc compiles and links by default; in Ada, the process is more complex.
After compilation, you need to call gnatbind on the .ali file of your main program. This generates a package that will take care of proper initialization of everything. To do that, gnatbind needs the .ali file of the Apples package! You can specify the .ali search directory with -aO.
Finally, gnatlink called on the main .ali file will link everything together. It figures out itself which object files and libraries it needs. It will append the necessary options to the linker, including the reference to libapples.a. It also compiles the package generated by gnatlink.
I don't know exactly what gnatlink will do under the hood; you could probably compile the package generated by gnatbind manually and then call the linker if you can figure out the proper arguments.
Normally SBT looks for the build files at ./build.sbt and ./project/Build.scala. Is it possible to specify an alternate project root, so I can build a project not in the current working directory? I'm looking essentially for an equivalent to mvn -f /path/to/pom.xml but the docs have not provided me with any obvious answers.
(I should note that I want to do this at runtime rather than compile time. Essentially, I want to use sbt run-main to run arbitrary classes from my project, since I hate manual classpath wrangling. For various boring reasons I may need to do this from arbitrary location since the code may need the working directory to be something other than the project directory, so using cd might not do what I want. It's so trivial in Maven - I just assumed, perhaps unfairly, that there would be a simple equivalent in SBT)
I have something like this. I have project definition at X/build.sbt, X/MyOtherDefinitionWithSpecialThing/build.sbt, X/MySuperPublishConfig/build.sbt.
But my point of view to the problem is opposite. Instead of specify location of ./build.sbt and ./project/Build.scala I specify location of path to resources. The result is the same. It looks like:
sourceDirectory <<= (baseDirectory) (_ / ".." / "src")
target <<= (baseDirectory) (_ / ".." / "target")
This is allow to create single project with multiple definitions. This is worked with nested/hierarchical projects. But I use symbolic links (Linux OS) for hierarchical projects.
There is a file tree of one of my SBT plugins. Multiple build definitions and only one src/...
.
|-build.sbt
|-project
|---project
|-----target
|-------...
|---target
|-----...
|-project-0.11
|---build.sbt
|---project
|-----project
|-------target
|---------...
|-----target
|-------...
|-project-0.12
|---build.sbt
|---project
|-----project
|-------target
|---------...
|-----target
|-------...
|-...
|-src
|---main
|-----scala
|-------org
|---------...
|---sbt-test
|-----...
|-target
|---...
If this not solution of your problem please elaborate why you don't want use 'cd' command ;-)
-- For the updated use case:
I use shell wrapper and I have symlink to this one in every SBT project:
#!/bin/sh
#
here=$(cd $(dirname "$0"); pwd)
if [ ! -e "${here}/build.sbt" ]
then
echo build.sbt lost
exit
fi
cd ${here}
LOCAL_BUILD=true sbt-0.12 "$#"
I simply write /path/to/my/project/sbt 'show name' for example or /path/to/my/project/sbt run-main in your case.
As I discovered from this other answer, the sbt-start-script plugin is a better tool for this than sbt run-main. You can simply run sbt stage and you get an invocation script, with classpaths resolved, at target/start. According to the documentation, it needs to be run from the build root directory for inter-project dependencies to work, but for my simple use cases, this doesn't seem to be a problem.
I'm doing an intro to programming course at university, and the language of choice is Ada. I'm coding in Kate and compiling with GNAT 4.6.3. We have to use teacher-provided libraries for our programs, like so:
with foo;
use foo;
Of course, then the file foo.adb has to be contained in the same directory as my source file. Since multiple projects depend on this one library, and I like to keep each project in its own subdirectory, I have to copy the library files into each new project. Not to mention have my library code and source code all in the same directory.
So is there any way to sort of go:
with ../../lib/foo
use ../../lib/foo
?
I've tried looking around a bit but all I've found is stuff about compiler options. I'd rather not have to mess around with those, especially because only certain projects are going to be requiring this particular library, so it wouldn't make sense to add it to a global compiler setting and have the compiler pointlessly searching paths it doesn't need to search.
I would use the GNAT Project facility with command-line gnatmake.
I just set up a little example (so I can be sure what I say works!). I have 3 directories; teacher/ contains the teacher-provided source, which I assume you won't want to change and may not have write access to anyway, jacks_lib/ contains teacher.gpr which points to teacher/ (you could put your own library code there as well) and jack/ contains your code main.adb and main.gpr.
jacks_lib/teacher.gpr:
project Teacher is
-- This project calls up the teacher-supplied source.
-- This is a list of paths, which can be absolute but
-- if relative are relative to the directory where this .gpr
-- is found.
for Source_Dirs use ("../teacher");
-- Keep the built objects (.ali, .o) out of the way. Use the -p
-- gnatmake flag to have directories like this built
-- automatically.
for Object_Dir use ".build";
end Teacher;
jack/main.gpr:
-- teacher.gpr tells where to find library source and how to build it.
with "../jacks_lib/teacher";
project Main is
-- for Source_Dirs use ("."); (commented out because it's the default)
-- Keep built objects out of the way
for Object_Dir use ".build";
-- Build executables here rather than in Object_Dir
for Exec_Dir use ".";
-- What's the main program? (there can be more than one)
for Main use ("main.adb");
end Main;
jack/main.adb:
with Foo;
procedure Main is
begin
null;
end Main;
Then, in jack/,
$ gnatmake -p -P main.gpr
object directory "/Users/simon/tmp/jacks_lib/.build" created for project teacher
object directory "/Users/simon/tmp/jack/.build" created for project main
gcc -c -I- -gnatA /Users/simon/tmp/jack/main.adb
gcc -c -I- -gnatA /Users/simon/tmp/teacher/foo.ads
gnatbind -I- -x /Users/simon/tmp/jack/.build/main.ali
gnatlink /Users/simon/tmp/jack/.build/main.ali -o /Users/simon/tmp/jack/main
I should add that I'm using GCC 4.7.0 on Mac OS X, but this should work fine with any recent GNAT.
Compiler options are the way you manage source code locations for a build--defining "search paths"--particularly the "-I" (include) option for gcc-based (like GNAT) and most other compilers.
If you're building from the command line, it's simply a matter of:
gnatmake -I../../lib/foo -Iother/path -Iyet/another/path project1_main.adb
gnatmake -I../../lib/foo -Isome/path -Iyet/another/path project2_main.adb
If you're using GPS (GNAT Programming Studio), then open the Project Properties dialog, select the Source dirs tab, and add the search paths there. (You can also directly edit a Project Properties file (".gpr") directly, but I rarely do that. YMMV.) Compiler settings are easily set up on a per-project basis, so there's no "global compiler setting" issue with which one would have to concern themself.
Its not been explicitly mentioned in the answers sofar, so I will say it:
The with & use clauses in Ada make no claims about the location of the withed & used units, only that they exist.
When it comes to actually finding the units, that is entirely up to the make scripts & .gpr files & compilation options or whatever else you come up with.
This is how Ada creates a degree of code portability (by not binding the code to a directory structure!), You just need to correct the compilation options :)
Also, learning about compiler options is almost never a bad idea, it will serve you well in Ada & many other languages.
Amplifying on #MarcC's answer, the GNAT User's Guide covers gnatmake in chapter 6, under ยง6.2 Switches for gnatmake: Source and library search path switches; the guide should be included with your distribution.
The gnatmake command is a convenient target for make, as shown in this example.
I am completely stuck with cmake. I am building big directory of
latex documents, so I want to get Makefile system with following
targets: prepare, build, archive. Main is that they must be recursive:
they present in every directory and run themself in all subdirectories.
But, unfortunally, if I manually say add_custom_target cmake complains
about duplicate targets. If I declare them only in root, then they do not
present in subdirectories. Unfortunately, I cannot reject cmake and start use
plain Makefiles.
Try using this:
http://www.cmake.org/Wiki/CMakeUserUseLATEX
This is my first try with cmake and I would like to have, if possible, some feedbacks about what I did since some problems remain.
In the CMakeLists.txt of the library folder, I created two makefile targets: configure-antlr3c and antlr3c. The first target runs the autotools configuration shell script, the second one runs the make executable to build the library:
# CMakeLists.txt in libantlr3c-3.1.3
add_custom_target(
configure-antlr3c
${SHELL_EXECUTABLE} configure
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
add_custom_target(
antlr3c
${MAKE}
DEPENDS configure-antlr3c
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
The main problem is thatconfigure-antlr3c target is always "out of date", so it will always be executed even if no changes happened. Moreover, I necessarily need to generate my cmake makefiles in a separate directory (not in the root directory of my project) to avoid overriding the autotools Makefile of the library...
Has anyone had this problem (building autotools projects with cmake) ? And if so, what have been your solutions ?
Thank you.
EDIT : Solution
In the root CMakeLists.txt:
include(ExternalProject)
ExternalProject_Add(
libantlr3c
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/lib/libantlr3c-3.1.3
CONFIGURE_COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/lib/libantlr3c-3.1.3/configure --prefix=${CMAKE_CURRENT_SOURCE_DIR}/lib/libantlr3c-3.1.3
PREFIX ${CMAKE_CURRENT_SOURCE_DIR}/lib/libantlr3c-3.1.3
BUILD_COMMAND make
BUILD_IN_SOURCE 1
)
I think that you'd be better off using the ExternalProject feature of cmake. I guess you have your project and have libantrl in a sub directory?
project
+- libantlr
+- mysrc
---- etc ----
If that's the case, you can do something like this in the top level CMakeLists.txt:
cmake_minimum_required(VERSION 2.8)
project(test)
include(ExternalProject)
ExternalProject_Add(libantlr
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/libantlr
CONFIGURE_COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/libantlr/configure --prefix=<INSTALL_DIR>
BUILD_COMMAND ${MAKE})
The <INSTALL_DIR> is expanded to something like libantlr-prefix, so things are installed in your build tree rather than in /usr/local, which is what autotools would do without a prefix.
I needed to do something similar but found it surprisingly difficult to get a working solution, despite the example provided here with the accepted answer, and code snippets provided in several other blog posts, the CMake support email listserv archives, etc. For the benefit of others who come across this question, here is my solution.
The external project we wanted to use is libmodbus, though I believe my solution is general enough to work with any project configured with the standard autoconf recipe of ./autoconf.sh && configure.sh && make && make install.
We wanted to add libmodbus as a submodule of our git repository. We added to our repository at the path <root>/opt/libmodbus. The CMake code to configure it is located in <root>/cmake/modbus.cmake, which is included from our root CMakeLists.txt using
# libmodbus
include(cmake/modbus.cmake)
The content of cmake/modbus.cmake is:
include(ExternalProject)
set(MODBUS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/opt/libmodbus)
set(MODBUS_BIN ${CMAKE_CURRENT_BINARY_DIR}/libmodbus)
set(MODBUS_STATIC_LIB ${MODBUS_BIN}/lib/libmodbus.a)
set(MODBUS_INCLUDES ${MODBUS_BIN}/include)
file(MAKE_DIRECTORY ${MODBUS_INCLUDES})
ExternalProject_Add(
libmodbus
PREFIX ${MODBUS_BIN}
SOURCE_DIR ${MODBUS_DIR}
DOWNLOAD_COMMAND cd ${MODBUS_DIR} && git clean -dfX && ${MODBUS_DIR}/autogen.sh
CONFIGURE_COMMAND ${MODBUS_DIR}/configure --srcdir=${MODBUS_DIR} --prefix=${MODBUS_BIN} --enable-static=yes --disable-shared
BUILD_COMMAND make
INSTALL_COMMAND make install
BUILD_BYPRODUCTS ${MODBUS_STATIC_LIB}
)
add_library(modbus STATIC IMPORTED GLOBAL)
add_dependencies(modbus libmodbus)
set_target_properties(modbus PROPERTIES IMPORTED_LOCATION ${MODBUS_STATIC_LIB})
set_target_properties(modbus PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${MODBUS_INCLUDES})
A component that uses libmodbus can declare its dependency as usual:
add_executable(hello_modbus main.cpp)
target_link_libraries(hello_modbus modbus)
A few notes:
This abuses the DOWNLOAD_COMMAND to perform the autogen.sh step. The git clean -dfX is probably not necessary (it is a leftover from an earlier version that used the BUILD_IN_SOURCE option. If you really want to download the code instead of using a git submodule, you'll need to modify this line appropriately.
We go to the trouble to force a static-only build of the library. Adjust your configure command line if you want shared libraries.
The set_target_properties command to set the IMPORTED_LOCATION will fail without the BUILD_BYPRODUCTS ${MODBUS_STATIC_LIB} declaration.
Likewise, the set_target_properties command to set the INTERFACE_INCLUDE_DIRECTORIES will fail without the file(MAKE_DIRECTORY ${MODBUS_INCLUDES}).