Sunday, September 26, 2010

Building Static Libraries and Test Program with Eclipse CDT

Based on sample code from a book, GNU/Linux Application Programming by M. Tim Jones, I have been reading to refresh myself on Linux programming, specifically in the C language, I have been trying to explore using the Eclipse CDT IDE.

I have managed to get projects set up in Eclipse for a simple application that prints numbers to a file. For the sake of practice, I have split the code into 1) a static library, libexp, based on the random number API library example from Chapter 7 2) a static library, libtherm, that wraps the random function with a function that returns the "temperature" 3) a simple executable that calls the temperature library function multiple times and prints the results.

Eclipse CDT
I downloaded and installed the Eclipse CDT Helios version for Linux (x86_64)
This requires a Java Runtime be installed on the machine. I had to install OpenJDK (1.6.0_18, 64-bit)

Project Structure

I started from an existing project I had created using the Automake/Autoconfig to set up a build environment. In this set up, I there was a single "project" directory with an app and lib subfolders containing respective source and build files.

As far as I can tell, the Eclipse CDT approach is that there is a single build target per Project. Perhaps if you develop the Makefile rather than have Eclipse create one you could do a different set up. Going with a completely Eclipse controlled build process I created separate projects for each static library and the executable file. All the source files went directly into each project directory under the workspace.


Build Artifacts

See Right-click Project > Properties > C/C++ Build > Settings > Build Artifact
For each of my projects libexp and libtherm, I set the Build Artifact properties as Artifact Type: Static Library, Artifact Name: ${ProjName}, Artifact Extension: a.

This results when you compile in Eclipse building and archiving the object files into libexp.a and

For temp-app the difference was the Build Artifact was set to Type Executable and there was no extension.

Referencing Libraries

In my case, the libtherm library references libexp library. The temp-app needed to reference both libtherm and libexp.
For libtherm Properties > C/C++ General > Paths and Symbols > References. I checked off libexp / Active.

This adds under the Includes > GNU C tab the Include directory /libexp.

I also updated the Properties > Project References to check off libexp project.

These steps also add GCC Compiler flags. Under Properties > C/C++ Build > Settings > GCC C Compiler > Includes it adds under -l included paths: "${workspace_loc:/libexp}"

If you click on the main GCC C Compiler heading All the options are listed:
-I"/home/me/workspace/libexp" -O0 -g3 -pedantic -Wall -c -fmessage-length=0


For the temp-app the procedure was more or less the same. Under the Project References, Path and Symbol References I added the two library projects libexp, libtherm.

Difference seems to be in the Executable type Project, the C/C++ Build > Settings has new options for GCC C Linker. Under Libraries -l I added "therm" and "exp" (GCC C Linker will prepend "lib" and append ".a"). Under the Library Search Path (-L) I added paths to the Debug directories in my other projects where the library artifacts were being built. For GCC C Linker All Options this results in:
-static -L"/home/me/workspace/libtherm/Debug" -L"/home/me/workspace/libexp/Debug" -o"appexp" ./test.o -ltherm -lexp

(note: one crucial step when linking libraries that may refer to each other: you must order them in a top down way. i.e. the libraries that have their own references to lower level libraries should be ordered first, so in my case it was -ltherm then -lexp. See this discussion on StackOverflow for more details: GCC C++ Linker errors: Undefined reference to ...')

Running the Project

Once the projects have been set up and you have a working build. You can try to run it.

See Run > Run Configurations
Add a new C/C++ Application to run e.g. "AppExp". Clicking run will execute the program with output going to your Eclipse Console panel

Monday, September 20, 2010

Java method to copy file to UNC network location

Small update on something I used for work. I needed a way to copy a file stored locally from one machine to another machine on the network with a shared folder. This is an all Windows environment.

To copy a file with Java (JRE 1.4 or higher) the method found here was really helpful:

...
//File in = new File("local-file.txt")
//File out = new File("\\\\network\folder\file.txt")
FileChannel inChannel = new
FileInputStream(in).getChannel();
FileChannel outChannel = new
FileOutputStream(out).getChannel();
try {
inChannel.transferTo(0, inChannel.size(),
outChannel);
}
catch (IOException e) {
throw e;
}
finally {
if (inChannel != null) inChannel.close();
if (outChannel != null) outChannel.close();
}
...

Credit: http://www.rgagnon.com/javadetails/java-0064.html

Used the "new" technique using the java.nio package for transferring the file. See FileChannel.transferTo()

The example in the how-to page describes copying an input File in to an output File out

All I had to do was make sure that Java would understand a UNC style network file path for the out File that I wanted to copy to. It was really as simple as that, and worked perfectly. Just remember to double-escape the "\" including the starting "\\" prefix in your UNC file path.

File out = new File("\\\\host-name\\path\\to\\target-file.zip");

That page also discusses an issue on Windows where files of sizes greater than 64 MB fail with an "java.io.IOException: Insufficient system resources exist to complete the requested service" error. I initially tried with the suggested workaround to transfer the file in 64 MB chunks, which worked. For the record the file transfer for a larger file (167 MB) took around 18sec with this method. Given that this issue is several years and java versions old I thought I would try this out without splitting up the file.

My development system is Windows 7, JRE 1.5.0_22, the target host with the network share was on Windows XP. Using the original transferTo method without splitting the file successfully copied my large test file with no errors. There was also a slight performance gain as the copy took about 16 seconds that time.

Doing a little research in the Oracle Sun Developer Network Bug Database, it seems this may have been an issue fixed in: 6431344. Or it may have been that this was an OS problem and doesn't manifest on the Win 7 platform. In either case seems to be working.

Related bugs:
6822107
6486606