Go Down

Topic: Conditional compiles in a library (Read 2163 times) previous topic - next topic

pico

I have a question that has already been asked, and answered, but it is in the old forum, so I can't add a reply to the original thread:

http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1285517993

I was trying to achieve the same thing as the OP, and tried the proposed solution in the penultimate post by "Coding Badly", but the library .cpp file is not locating the mylib_options.h file that in the sketch directory during the build. The compiler error I am seeing is:

Code: [Select]

error: mylib_options.h: No such file or directory


I appreciate the original thread is a few years old, and so this might have worked in an earlier version of the IDE, but then stopped working. (The error above is with version 0023.)

Is there a different way of achieving the same result (i.e., having a library .cpp or .h file pick up a .h file from the sketch directory via a #include directive) in more recent versions of the IDE?

WiFi shields/Yun too expensive? Embeddedcoolness.com is now selling the RFXduino nRF24L01+ <-> TCP/IP Linux gateway: Simpler, more affordable, and even more powerful wireless Internet connectivity for *all* your Arduino projects! (nRF24L01+ shield and dev board kits available too.)

PaulS

Quote
Is there a different way of achieving the same result (i.e., having a library .cpp or .h file pick up a .h file from the sketch directory via a #include directive) in more recent versions of the IDE?

It would really be necessary to see how you are trying to do this. For instance, there is a difference between

Code: [Select]
#include "someFile.h"

and

Code: [Select]
#include <someFile.h>

pico

#2
Jul 16, 2012, 12:27 pm Last Edit: Jul 16, 2012, 12:43 pm by pico Reason: 1
I understand that. I've tried both

Code: [Select]

#include "mylib_options.h"


as well as

Code: [Select]

#include <mylib_options.h>


to see if that would widen the search path.

As well as :

Code: [Select]

#include "./mylib_options.h"
#include ".\mylib_options.h"
#include ".\\mylib_options.h"


and even things like


Code: [Select]

#include "../mylib_options.h"
#include "..\mylib_options.h"
#include "..\\mylib_options.h"


since the library source seems to be copied to a subdirectory of a the main temporary build directory during the build process.

But all no go, and I'm getting bored guessing.

If you know something that works for even a simple case, I can adapt that. Basically, if there is a way for a .h file or .cpp file located in

Code: [Select]

<Arduino>\libraries\mylib


to #include a file from the compiling sketch directory when imported as a library by that sketch, let me know.

Is there a macro that expands to point to the sketch directory during the build process, for example?

I wouldn't be adverse to putting in something like

#include "$SKETCH_DIR/mylib_options.h"

if that would do the trick. I couldn't find any reference to macros that expand to specific directory paths, however.





WiFi shields/Yun too expensive? Embeddedcoolness.com is now selling the RFXduino nRF24L01+ <-> TCP/IP Linux gateway: Simpler, more affordable, and even more powerful wireless Internet connectivity for *all* your Arduino projects! (nRF24L01+ shield and dev board kits available too.)

pico

BTW, just as a test, I did try to hardcode the full directory path to a sketch directory, as in

Code: [Select]

#include "/Arduino/arduino-0023/MySketches/nRF24l01/ib_nRF24l01_TX_RX_1c3c/mylib_options.h"


and that worked as expected. So the macro expansion idea should work fine, if there was such a macro defined that could be used.
WiFi shields/Yun too expensive? Embeddedcoolness.com is now selling the RFXduino nRF24L01+ <-> TCP/IP Linux gateway: Simpler, more affordable, and even more powerful wireless Internet connectivity for *all* your Arduino projects! (nRF24L01+ shield and dev board kits available too.)

pico

Uh oh. I thought this was a question that might be nailed pretty quickly. Have I asked a "hard" question, as it turns out, or just an extremely dull one?  ;)
WiFi shields/Yun too expensive? Embeddedcoolness.com is now selling the RFXduino nRF24L01+ <-> TCP/IP Linux gateway: Simpler, more affordable, and even more powerful wireless Internet connectivity for *all* your Arduino projects! (nRF24L01+ shield and dev board kits available too.)

Coding Badly


09:31:10 - 03:46:00 => less than six hours.  Patience Grasshopper.  Some forum participants require sleep.

As far as I know, the technique never worked.  The sketch directory is not included in the search path.

The next best solution is to use "variants".  Unfortunately, that requires modifying boards.txt and is not practical if most sketches will have unique settings.

pico

So there's no known way in the IDE to affect conditional compilation for a library by compiling from a particular sketch directory?

This is straightforward to do achieve outside the IDE using e.g. makefiles, but I was hoping to maintain compatibility with the IDE in case someone (maybe even me) wants to use the IDE rather than Make to build a sketch later on.

Ideally, the way it should work is the way you suggested it might work in your original post a couple of years back: If the library sees the include file in the sketch directory, use that; otherwise, use a default config file of the same name in the library itself. This would be an easy and natural way to implement "default options".

Next best, would require the user to copy the default config file to the sketch directory manually. Not quite as elegant, but no biggy.

Oh well. If there isn't actually a way of doing any of this (I am assuming implicit in what you say is that there isn't an accessible macro that expands to the sketch directory), perhaps you could move this entire thread over to the "Suggestions to improve the IDE" subforum.

In the meantime, makefiles it is. I wonder if the plug-in for eclipse could do this as well?
WiFi shields/Yun too expensive? Embeddedcoolness.com is now selling the RFXduino nRF24L01+ <-> TCP/IP Linux gateway: Simpler, more affordable, and even more powerful wireless Internet connectivity for *all* your Arduino projects! (nRF24L01+ shield and dev board kits available too.)

bperrybap

yeah, the way the IDE handles include paths sucks in my opinion.
I pulled my hair out with this a few years ago for several months and never came up with anything.
The best I ended up with is a config file in the library directory that the user can edit.
But since it is in the library directory, it is global to all sketches that use the library.

A big problem in my opinion is that the IDE "learns" the include path from scanning
the sketch. This same path is used for the libraries. That is the brain dead part.
That method makes it impossible for a library to call another library without having the
sketch including a header from the sub libraries directory.

There are many interesting things that could be done if the IDE simply added
the current sketch directory, the path to the base of the users library directory and the
path to the base of the IDE's  library directory to the include path.
(in that order to the front of the include path) to all things being compiled (sketches and libraries).

Then you could do what you want (I want to do this too).
It would also allow using other library class headers by referencing them as:
#include <libname/header.h>
for example
#include <Wire/Wire.h>
would include the wire header. etc...

For configuration options, a library could include an options header file and the local version in
the users sketch directory would override a default one in the library directory since
the users sketch directory is earlier in the search path.


--- bill

pYro_65

#8
Jul 17, 2012, 08:51 am Last Edit: Jul 17, 2012, 09:31 am by pYro_65 Reason: 1
I have spent a few minutes tacking the situation as I may have a need for something similar in the near future.
Here is the solution I have come up with:

What happens is myLib.h defines a bunch of defines if not pre-set by the sketch. Allowing the client to set options...
To verify the cpp can see the changed values uncomment the OPTION_1 define in the sketch code below, it will generate a static assertion informing you the option has changed. ( see errors generated in the IDE output window. )

Sketch file
Code: [Select]
//#define OPTION_1 'BB'

#define SKETCH_PASS
#include <myLib.cpp>
#undef SKETCH_PASS
#include <myLib.h>

myObj m_Obj;

void setup(){
 Serial.begin( 9600 );
}

void loop(){
 Serial.println( m_Obj.myFloat );
}


myLib.h Inside libraries/myLib/
Code: [Select]
 #ifndef OPTION_0 //bool
   #define OPTION_0    false
 #endif
 #ifndef OPTION_1 //int
   #define OPTION_1    'AA'
 #endif
 #ifndef OPTION_2 //string
   #define OPTION_2    "AA"
 #endif
 #ifndef OPTION_3  //char
   #define OPTION_3    'A'
 #endif
 #ifndef OPTION_4 //float
   #define OPTION_4    123.4f
 #endif

 #ifndef CLASS_MYOBJ
   #define CLASS_MYOBJ
 class myObj{

   public:
     myObj( void );
     
     static const float myFloat = OPTION_5;
 };
 #endif

//EOF


myLib.cpp  Inside libraries/myLib/
Code: [Select]
#ifdef SKETCH_PASS

 #include "myLib.h"

 template <bool> struct CompileTimeError;
 template <> struct CompileTimeError<true> {};
 #define static_assert(cond,msg) { CompileTimeError<!!(cond)> ERROR_##msg; (void) ERROR_##msg; }

 myObj::myObj( void )
   {
     static_assert( ( OPTION_1 == 'AA' ), OPTION_1_HAS_CHANGED );
     return;
   }
#endif


EDIT: This may not be what you are looking for, the cpp is effectively inlined. I'm still looking for alternatives as this is similar to having the cpp contents in the header rather than two separate files.

pico

pYro_65,

This is very creative, if I understand how it works (I think I do!)

These lines

Code: [Select]

#define SKETCH_PASS
#include <myLib.cpp>
#undef SKETCH_PASS
#include <myLib.h>


manually include the library source so it becomes "inline" with the sketch code, and so when it tries to pick up the config file, it will looking in the sketch directory (because it is really part of the sketch file, now).

OTOH, the guard #ifdef in the library .cpp means that the preprocessor will be seeing that as an empty file when the time comes to link in the library (to avoid duplicate symbol references at link time.)

The only thing I'm uncertain about is how setting the SKETCH_PASS macro in the sketch file is seen by the preprocessor pass over the .cpp in the library file.

Actually, as I was typing that, I see the trick -- because it actually is a #ifdef, and it is _always_ undefined in the  library source that is not inlined in the sketch file, it doesn't matter -- undefined is undefined.

In which case, if I am right,  the

Code: [Select]

#undef SKETCH_PASS


line in the sketch file actually isn't necessary... no matter what you define SKETCH_PASS in the sketch file, it's always going to be undefined in the (un-inlined) library source!

I'm going to have to try this out. You have a twisted mind. A brilliant mind perhaps, but twisted nonetheless!
WiFi shields/Yun too expensive? Embeddedcoolness.com is now selling the RFXduino nRF24L01+ <-> TCP/IP Linux gateway: Simpler, more affordable, and even more powerful wireless Internet connectivity for *all* your Arduino projects! (nRF24L01+ shield and dev board kits available too.)

pYro_65

#10
Jul 17, 2012, 09:46 am Last Edit: Jul 17, 2012, 09:50 am by pYro_65 Reason: 1
You are right, I changed the files to conform with undefined macros, this fixes one part:

Sketch
Code: [Select]
//#define OPTION_1 'BB'

#define SKETCH_PASS 4
#include <myLib.cpp>
#include <myLib.h>


myLib.cpp
Code: [Select]

#if SKETCH_PASS > 0

  //Code

#endif

pico

#11
Jul 17, 2012, 09:51 am Last Edit: Jul 17, 2012, 09:53 am by pico Reason: 1
I saw your edit, and it may be OK in general to inline the code... perhaps the #include for the inline code could appear towards the end of the file if there were problems putting it at the top.

I'm trying to think of a case where the inlining wouldn't work... can't think of one off the top of my head. Maybe in some multi-file source code situations...

Anyway, this is very worthwhile to experiment with, even if just for fun! :-)

Bperrybap's analysis seems right on the money, though, for the way this should be treated in the first place...
WiFi shields/Yun too expensive? Embeddedcoolness.com is now selling the RFXduino nRF24L01+ <-> TCP/IP Linux gateway: Simpler, more affordable, and even more powerful wireless Internet connectivity for *all* your Arduino projects! (nRF24L01+ shield and dev board kits available too.)

pYro_65

#12
Jul 17, 2012, 09:55 am Last Edit: Jul 17, 2012, 10:01 am by pYro_65 Reason: 1
Problems may arise when ( code bloat / errors ) two sketch files use the same library files.
Or if the library includes a file that also uses the stuff defined in the cpp, it could have undefined reference errors.

It could lead to the whole library being inlined, this isn't a problem in some circumstances. My library is entirely template driven so everything is inlined.

pico

#13
Jul 17, 2012, 10:04 am Last Edit: Jul 17, 2012, 10:15 am by pico Reason: 1

Problems may arise when ( code bloat / errors ) two sketch files use the same library files.

I think if only one sketch file actually does the #include xxx.cpp inlining, and the other doesn't, it should be OK. The second one would still just see an empty library source file, so no bloat (it still needs to do a #include <xxx.h>, of course, to pick up the header file definitions).

Quote

Or if the library includes a file that also uses the stuff defined in the cpp, it could have undefined reference errors.


Yeah, I think there will be problems in the general multi-file case. But nothing a bit of judicious guard #defines and structuring shouldn't be able to work around, I suspect.
WiFi shields/Yun too expensive? Embeddedcoolness.com is now selling the RFXduino nRF24L01+ <-> TCP/IP Linux gateway: Simpler, more affordable, and even more powerful wireless Internet connectivity for *all* your Arduino projects! (nRF24L01+ shield and dev board kits available too.)

pico

#14
Jul 17, 2012, 12:27 pm Last Edit: Jul 17, 2012, 12:56 pm by pico Reason: 1
OK, just to confirm this all works:

In the sketch .pde file, I have

Code: [Select]

#define SKETCH_PASS

#include "nordicRF24_config.h"

#include <nordicRF24.cpp>

#include <nordicRF24.h>


Around the nordicRF24.cpp library code, I have

Code: [Select]

#ifdef SKETCH_PASS

//#include "nordicRF24_config.h"

#include <WProgram.h>
#include "nordicRF24.h"

[...]

#endif


Note that in the .cpp I have commented out the #include for the configuration header file. I found that even though the .cpp is inlined in the .pde file which is in the sketch directory, the preprocessor looks for the  "nordicRF24_config.h" in the same file as the .cpp, i.e., the library directory (which is not what I want.)

So the

#include "nordicRF24_config.h"

must appear in the sketch .pde file, or it won't be picked up from the sketch directory.

(So I was incorrect in my assumption above that because the .cpp was inlined, that would be enough to change the searched location for any #includes in the .cpp file.)

There was a bit of messing around required to add some guard code to libraries that were #included from both the main sketch file and the inlined .cpp, file, but all straightforward and nothing serious. Some extern decalarations in the main sketch file which were referring to symbols in the library .cpp file were not needed now that they were no longer extern (inlining will do that for you)!

But I can confirm I've now got two different sketches with two quite different configuration files working with this system.

Next, I am going to try to implement a "default" system for #define macro values by putting in a bunch of directives like

Code: [Select]

#ifndef NODE_TYPE
#define NODE_TYPE MASTER
#endif


in a default_config.h in the library directory, and put a #include "default_config.h" directive near the top of the .cpp file to pick it up.

That will (should) mean that in the config header file in the sketch directory, I only have to specify the #define values that I want to override from the default values.

Which I think will make the system quite workable (if bizarre and Byzantine in a way only working with the Arduino IDE could produce)!

Cheers, pYro_65. :)

WiFi shields/Yun too expensive? Embeddedcoolness.com is now selling the RFXduino nRF24L01+ <-> TCP/IP Linux gateway: Simpler, more affordable, and even more powerful wireless Internet connectivity for *all* your Arduino projects! (nRF24L01+ shield and dev board kits available too.)

Go Up