Using multiple versions of Boost

Hello, Assume that you have multiple developers developing plugins for a commercial package and that we recently deployed boost 1.34 to our facility. For some informed developers, they will switch over to 1.34 whereas others might not know and stick with 1.33.1 or even 1.32. Now imagine loading in all these plugins into the 3rd party app, wouldn't there be some sort of symbol problems especially with boost run-time libraries (take the regex library for example) ?? Does anyone have suggestions on handling this? A typical way we solve versioning our open source libraries is to add a namespace to it. eg. SomeLibrary::v1_33_1 (although we never did attempt to fiddle around with boost). Any other ideas? Thanks!

Piyo wrote:
Hello,
Assume that you have multiple developers developing plugins for a commercial package and that we recently deployed boost 1.34 to our facility. For some informed developers, they will switch over to 1.34 whereas others might not know and stick with 1.33.1 or even 1.32. Now imagine loading in all these plugins into the 3rd party app, wouldn't there be some sort of symbol problems especially with boost run-time libraries (take the regex library for example) ??
Does anyone have suggestions on handling this? A typical way we solve versioning our open source libraries is to add a namespace to it. eg. SomeLibrary::v1_33_1 (although we never did attempt to fiddle around with boost). Any other ideas?
I did some work on this in boost a few years back. But my problem went away as it was decided not to use boost in product libraries :-( but only use it in applications and tools. Basically my approach was to modify the $BOOST_ROOT/tools/bcp tool so it would wrap boost libs in alternative namespace to "boost". I have some code for bcp which seems to work good, but I never completed a full regression test with all of boost. Using boost types in interfaces to your lib/plug-in will pose some challenges with this approach. Otherwise I think it is the way to go. -- Bjørn

On Monday 14 May 2007 22:51:18 Piyo wrote:
Assume that you have multiple developers developing plugins for a commercial package and that we recently deployed boost 1.34 to our facility. For some informed developers, they will switch over to 1.34 whereas others might not know and stick with 1.33.1 or even 1.32. Now imagine loading in all these plugins into the 3rd party app, wouldn't there be some sort of symbol problems especially with boost run-time libraries (take the regex library for example) ??
This is not specific to Boost at all. The point is that as soon as your plugin's interface depends on the version of another library you can't reliably use a different version thereof. What does that mean in practice? Example 1, working: //interface void function(); //implementation void function() { // use Boost here } Example 2, not working: //interface void function( boost::function0<void> fn); //implementation void function( boost::function0<void> fn) { // use Boost here } The reason this doesn't work is that different Boost versions have different implementations of boost::function. As already said, this not only affects Boost, but also other libraries. For example if you replace the native standard library with a newer version, or with STLport, or if you then use the diagnostic version (special debug mode) you each time get different implemetations for std::string. Also, of course, the used compiler and possibly its settings make a difference. Uli

On 5/15/07, Ulrich Eckhardt <doomster@knuut.de> wrote:
On Monday 14 May 2007 22:51:18 Piyo wrote:
[snip]
This is not specific to Boost at all. The point is that as soon as your plugin's interface depends on the version of another library you can't reliably use a different version thereof.
If you dont have any boost in namespace boost, but in different namespaces, e.g. boost_1_33_1, boost_1_34_0, then there is no problem at all. You can have the same translation unit using both versions of boost.
What does that mean in practice?
Example 1, working:
//interface void function(); //implementation void function() { // use Boost here }
The worst problem is that even the example 1 is not guaranteed to work. If whoevers use the function interface is using newer version of boost it will lead to ODR violations and very hard to track bugs. And it may even be that the user is not the same as the developer of function. And can't track different uses of boost.
[snipped]
As already said, this not only affects Boost, but also other libraries. For example if you replace the native standard library with a newer version, or with STLport, or if you then use the diagnostic version (special debug mode) you each time get different implemetations for std::string. Also, of course, the used compiler and possibly its settings make a difference.
The problem is that boost may be used as an implementation detail. But its use must be documented anyway and a version lock-in must occur. Which is desirable for a library developer.
Uli
Best regards, -- Felipe Magno de Almeida

Felipe Magno de Almeida wrote:
Sorry answering to myself.
On 5/15/07, Felipe Magno de Almeida <felipe.m.almeida@gmail.com> wrote:
[snip]
Which is desirable for a library developer.
Sure I meant *un*desirable.
Best regards,
I agree with Ulrich that the problem is not specific to Boost but the solutions that we use impact the library in question. What I guess I am saying is that if Boost were to adopt user- definable versioned namespaces, it would help a lot. Here is what we have been doing for other open source libraries: example: user-config.h --------------------------------- #define SITE_SPECIFIC_NAMESPACE_BEGIN namespace v1_33_1 { #define SITE_SPECIFIC_NAMESPACE_END } --------------------------------- example: boost_foo.h --------------------------------- #include "user-config.h" namespace boost { SITE_SPECIFIC_NAMESPACE_BEGIN // do you regular stuff here SITE_SPECIFIC_NAMESPACE_END } --------------------------------- For people who do not care, SITE_SPECIFIC_NAMESPACE is simply nothing and it reverts to your unversioned boost. Otherwise, if we do care and put our versioning in it, we can now distinguish different interfaces and plus have the compiler warn us of such mixing. Thoughts? concerns? Any other alternatives?

Piyo wrote:
Felipe Magno de Almeida wrote:
Sorry answering to myself.
On 5/15/07, Felipe Magno de Almeida <felipe.m.almeida@gmail.com> wrote:
[snip]
Which is desirable for a library developer.
Sure I meant *un*desirable.
Best regards,
I agree with Ulrich that the problem is not specific to Boost but the solutions that we use impact the library in question. What I guess I am saying is that if Boost were to adopt user- definable versioned namespaces, it would help a lot. Here is what we have been doing for other open source libraries:
example: user-config.h --------------------------------- #define SITE_SPECIFIC_NAMESPACE_BEGIN namespace v1_33_1 { #define SITE_SPECIFIC_NAMESPACE_END } ---------------------------------
example: boost_foo.h --------------------------------- #include "user-config.h"
namespace boost { SITE_SPECIFIC_NAMESPACE_BEGIN // do you regular stuff here
SITE_SPECIFIC_NAMESPACE_END } ---------------------------------
For people who do not care, SITE_SPECIFIC_NAMESPACE is simply nothing and it reverts to your unversioned boost. Otherwise, if we do care and put our versioning in it, we can now distinguish different interfaces and plus have the compiler warn us of such mixing.
This helps, but it is ugly and in my opinion far from bullet proof. You have to consider other aspects as soon as you expose 2 versions of the same header files. E.g. are all macro/precompiler definitions compatible?
Thoughts? concerns? Any other alternatives?
You could read this thread. http://lists.boost.org/Archives/boost/2005/09/93902.php If there is any interest I can dig out the modified bcp code. As I remember it worked well for replacing namespace boost and all references to it with whatever you like, e.g. boost with boost_1_32. It would also replace any boost macro BOOST_XX with BOOST_1_32_XX. This worked nicely with running the boost tests in the boost/status directory. I was down to a few errors which seemed related to Jamfiles. One effect of this approach is that the code using boost would have to use the new namespace and macros. It would be annoying to use versioned names, hence I would use mylib_boost, boost_mylib, or boost::mylib. Then you have to hope your clients are not using more than one version of mylib in the same app ;-). In a situation where you have a lot of plug-in components, using versioned names would allow them to share boost .dll/.so file dependencies. The latest version of the bcp code I worked on attempted to do nested namespaces like boost::mylib. This was only partly completed using boost::xpressive with encouraging results. -- Bjørn
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

On Wed, 16 May 2007 05:51:22 +0200 Bjørn Roald <bjorn@4roald.org> wrote:
You could read this thread.
Interesting. I have a similar problem. I build an distribute a library that is used throught the company, and it uses boost internally. Other groups in the company may be using different versions of boost than the one this library is built with. For that reason, the interface MUST NOT use anything in boost. The implementation can only use things that instantiate templates based on internal types (i.e. not visible in the interface). This is really bad. If sorely wish there was a way to use different versions of boost. One of the things I did in this particular library, is version itself with a macro like so in say, foo/common.hpp #define FOO_NAMESPACE PP_build_namespace(foo, FOO_MAJOR_VERSION) namespace FOO_NAMESPACE { } namespace foo = FOO_NAMESPACE; This means that all code can use the alias "foo" as the namespace name. The only problem is that the namespace can not be reopened with "foo" but must be reopened with the macro name... namespace FOO_NAMESPACE { namespace detail { // ... } } This does not seem to be too big of a problem, since opening the namespace means you are changing it, and it should not be done in a willy-nilly fashion. This allows multiple versions to be used in the same application without ODR violations, and also makes sure the proper libraries are linked, because the linking will fail if the right version is not provided. It allows backward compatible interfaces as long as the maintainer is responsible about naming the versions. I'm not sure how much traction a sudggestion like this would gain in the boost community, but I certainly would love to see some way of versioning the libraries. I am certainly open to any ideas of a BETTER approach, both for my own libraries, boost, and other third party libraries with similar problems. How do YOU (plural) manage different versions of third party libs in your applications? What I would REALLY like to see is expanded language support for this concept. Maybe allow an alias to open the namespace for which it is currently aliased...

Jody Hagins wrote:
One of the things I did in this particular library, is version itself with a macro like so in say, foo/common.hpp
#define FOO_NAMESPACE PP_build_namespace(foo, FOO_MAJOR_VERSION) namespace FOO_NAMESPACE { } namespace foo = FOO_NAMESPACE;
This means that all code can use the alias "foo" as the namespace name.
The only problem is that the namespace can not be reopened with "foo" but must be reopened with the macro name...
FWIW, The Xerces C++ API uses the same techinique to version their libraries. It also has issues with forward declaration, as that, also, needs to be done in the correct namespace. /Marcus

On Wed, 16 May 2007 16:25:25 +0200 Marcus Lindblom <macke@yar.nu> wrote:
FWIW, The Xerces C++ API uses the same techinique to version their libraries. It also has issues with forward declaration, as that, also,
needs to be done in the correct namespace.
Right... What type of feedback has there been to this approach among the Xerces users/developers? More meaningful, as it pertains to boost, is it something that has been received warmly, or with disdain? I know nothing about Xerces, so... how is its size relative to the size of boost, and does this approach seem to be something viable for boost?

Jody Hagins wrote:
On Wed, 16 May 2007 16:25:25 +0200 Marcus Lindblom <macke@yar.nu> wrote:
FWIW, The Xerces C++ API uses the same techinique to version their libraries. It also has issues with forward declaration, as that, also,
needs to be done in the correct namespace.
Right... What type of feedback has there been to this approach among the Xerces users/developers?
More meaningful, as it pertains to boost, is it something that has been received warmly, or with disdain? I know nothing about Xerces, so... how is its size relative to the size of boost, and does this approach seem to be something viable for boost?
I don't have sufficient knowledge to answer in full. I've only used Xerces a little bit. So, my own subjetive 0.02€: Xerces is pretty large, it has a lot of classes, etc. However, it's completely void of templates, if that would matter. The techinique appears very non-intrusive for users of a single version (except for forward declarations). I'm sure one can come up with a recommeded scheme that would simplify things (i.e. for the author FooLib to define FOOLIB_BOOST_VERSION and use that). However, there are a few places in boost that export "global" variables, such as the placeholders (IIRC they go in the anonymous namespace). It could add some additional testing burden. (Worst case: Test to include every boost file with every other boost file, both of every version supporting this scheme.) But I'm sure there are others who are more familiar with this technique and it's potential pitfalls. Cheers, /Marcus

Marcus Lindblom wrote:
Xerces is pretty large, it has a lot of classes, etc. However, it's completely void of templates, if that would matter.
The techinique appears very non-intrusive for users of a single version (except for forward declarations).
I'm sure one can come up with a recommeded scheme that would simplify things (i.e. for the author FooLib to define FOOLIB_BOOST_VERSION and use that). However, there are a few places in boost that export "global" variables, such as the placeholders (IIRC they go in the anonymous namespace).
It could add some additional testing burden. (Worst case: Test to include every boost file with every other boost file, both of every version supporting this scheme.)
But I'm sure there are others who are more familiar with this technique and it's potential pitfalls.
Cheers, /Marcus
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
I am open to any form of solution whether it be macro-based or bcp or Xerces style. I just find it sad that I am stopping 1.34 from entering our company on purpose because I do not think that adding to our existing 1.32 and 1.33.1 installations will help. Is there anyway we can get the core boost developers to look into this? Since I am the only boost-evangelist here, I can't imagine me stopping progress over something like this. HELP!! :)

Piyo wrote:
I am open to any form of solution whether it be macro-based or bcp
If you intent to test the bcp approach, my code is a good way to start. I looked it up, and I have several versions of it available. The latests version introduces boost::expressive as dependency to bcp to handle changing boost to nested namespaces like boost::1.34.0. This code is as far as I remember not complete, so the best start would be the earlier version based on boost::regexp only. If this is what you want to try, I will send you the code. I did a quick diff against a resent cvs version of boost, and it seems that there are a few changes to bcp we want to merge in. If you are interested and give me a few days, I will find time to merge and test the merged version a bit for you. If a bcp based solution becomes useful, I think it should be considered to be included in the tool in the boost distribution. As of now I think we need some more prof of the usefulness of the concept, and I think documentation is needed as to the best ways to use such a tool. Maybe some conventions can be introduced for naming and how the tool is used in the configuration management. Here is a simple sketch of my ideas: boost.org | my_place.com ------------+--------------------------+-------------------------------- central SCM | local CM/SCM | build environment ------------+--------------------------+-------------------------------- repo <--svn--> namespace boost | --bcp-> namespace mylib_boost | | | do your boost work here | bcp used as a build step only As the figure illustrate, bcp is a one way tool when you change the namespace. I feel strongly that the tool shall be used as a build step rather than keeping the modified code under local source control. The better way is to do modifications, patches, testing and contributions for boost as other boost users. Then use bcp as a build-time tool. To reduce overhead introduced by this build-time, I added an option to my bcp version which in case the target file exist only will overwrite if file content changes. This reduces build time dramatically for changes after initial build.
or Xerces style. I just find it sad that I am stopping 1.34 from entering our company on purpose because I do not think that adding to our existing 1.32 and 1.33.1 installations will help. Is there anyway we can get the core boost developers to look into this? Since I am the only boost-evangelist here, I can't imagine me stopping progress over something like this.
HELP!! :)
Help is offered ;-) Anyone ready to try this can mail me off the list if you prefer. -- Bjørn This file contains notes regarding enhansements to BOOST_ROOT/tools/bcp to support replacement of the boost namespace in sourcecode copied from the boost distrubution. ==================== HOWTO ====================== A new option: --namespace=ns set a namespace that will replace 'boost' is added to the bcp tool. This allow the user to specify a namespace to use as replacement for boost in exported source code. example use:
cd ~/src/boost mkdir -p /tmp/testbcp/mylib tools/bcp/run/bcp -cvs -namespace=mylib filesystem /tmp/testbcp/mylib
later you can use
tools/bcp/run/bcp -cvs --namespace=spoost --diff-only filesystem /tmp/testbcp/mylib
to only do the changes You can add boost-build.jam Jamfile Jamrules to the module-list on the bcp command line to get the top level build files needed to build the exported code. If you change the following 2 lines in the exported Jamfile [ glob-tree $(BOOST_ROOT)/boost/compatibility/cpp_c_headers : c* ] [ glob-tree $(BOOST_ROOT)/boost : *.hpp *.ipp *.h *.inc ] to [ glob-tree $(BOOST_ROOT)/mylib/compatibility/cpp_c_headers : c* ] [ glob-tree $(BOOST_ROOT)/mylib : *.hpp *.ipp *.h *.inc ] you can actually build the exported libraries with bjam. NOTE: see limitation regarding Boost.Build ================== TODO ========================= 1. Support replacing namespace boost with nested namespace, e.g. namespace boost ====> namespace mylib::boost or namespace boost ====> namespace mylib::stuff::platform 2. Better Boost.Build and Jamfile support in export =============== LIMITATIONS ====================== 1. Only replacement of boost namespace with single level namespace is suported. No replacement with nested namespace supported jet. 2. Boost.Build export not suported properly. Buildfiles are still configured for the "boost" name after export. E.g. libriaries built contain the string "boost" in their filenames.

Bjørn Roald wrote:
If a bcp based solution becomes useful, I think it should be considered to be included in the tool in the boost distribution.
Sounds interesting enough to be worth adding: regex based search and replace of namespaces should be easy enough?
As the figure illustrate, bcp is a one way tool when you change the namespace. I feel strongly that the tool shall be used as a build step rather than keeping the modified code under local source control. The better way is to do modifications, patches, testing and contributions for boost as other boost users. Then use bcp as a build-time tool.
To reduce overhead introduced by this build-time, I added an option to my bcp version which in case the target file exist only will overwrite if file content changes. This reduces build time dramatically for changes after initial build.
Also a good idea, as long as it's an option: sometimes you want to overwrite the existing file :-) John.

John Maddock wrote:
Bjørn Roald wrote:
If a bcp based solution becomes useful, I think it should be considered to be included in the tool in the boost distribution.
Sounds interesting enough to be worth adding: regex based search and replace of namespaces should be easy enough?
Well -namespace=boost_1_34_0 actually does a bit more. It renames directories and include directives so stuff like #include <boost/filesystem/path.hpp> becomes #include <boost_1_34_0/filesystem/path.hpp> and should work. It also replaces all instances of BOOST_ prefix in preprocessor symbol definitions and references with BOOST_1_34_0_ and processes Jamfiles as well so my hope was that all should build, test, and work without touch up work. Also in the general case ../bin/boost/libs/filesystem/build/libboost_filesystem.a/gcc/debug/libboost_filesystem-gcc-d-1_33_1.a should become ../bin/boost_1_34_0/libs/filesystem/build/lib/boost_1_34_0_filesystem.a/gcc/debug/libboost_1_34_0_filesystem-gcc-d-1_33_1.a In this specific case the additional 1_34_0 substrings are just annoyance, so I guess we need to treat it as a special case when we have a version part of the new namespace. But as I said library path naming is not implemented yet. I guess this can be done by treating some configuration files for the build system. This may be outside the scope of the bcp tool. So.. given all this, are you still sure this is worth adding? Also, is -namespace the best name for the command line option? I have working code and will happily merge with 1.34.0 version of bcp and provide you a patch. Then you can use what you like, fix what you like to fix, and dump what you don't like :-) As mentioned earlier, my work on this stopped as my urgent need went away 2.5 years ago. My plan with the code was proof of concept prototyping. So I used the brute force approach where I used regexp replace aggressively, then treated a few cases specially as I hit them. This worked surprisingly well, so my intention of a next step to move to a more robust c++ parsing may not be needed at all. Anyway, there are a few special cases, and the organization of that code is probably something that should be re-factored to be more in line with the rest of the bcp code. Maybe even using configuration files rather than hard-coding. Other issues: From my very limited knowledge of boost::regexp, I did not find a way to solve replacing namespace boost { ... } with namespace boost { namespace 1_34_0 { ... }} // end namespace boost::1_34_0 Where searching for the ending curly braces is the challenge. I had some success experimenting with boost::expressive, but this code is far from ready. I am also a lot more concerned with parsing issues and robustness as I envision many "reasonable" ways to break the matching. So my suggestion is to start with the boost::regexp code which works well for the simpler use-case. If anybody want to work on the nested namespace use-case, then I am happy to provide them with my code. I do however not have time to spend on this now, sorry.
As the figure illustrate, bcp is a one way tool when you change the namespace. I feel strongly that the tool shall be used as a build step rather than keeping the modified code under local source control. The better way is to do modifications, patches, testing and contributions for boost as other boost users. Then use bcp as a build-time tool.
To reduce overhead introduced by this build-time, I added an option to my bcp version which in case the target file exist only will overwrite if file content changes. This reduces build time dramatically for changes after initial build.
Also a good idea, as long as it's an option: sometimes you want to overwrite the existing file :-)
This code will arrive with the patch. -- Bjørn

Bjørn Roald wrote:
#include <boost_1_34_0/filesystem/path.hpp>
and should work. It also replaces all instances of BOOST_ prefix in preprocessor symbol definitions and references with BOOST_1_34_0_ and
cool you just answered my coworker's question without seeing it :)
Other issues:
From my very limited knowledge of boost::regexp, I did not find a way to solve replacing
namespace boost { ... }
with
namespace boost { namespace 1_34_0 { ... }} // end namespace boost::1_34_0
Hmmm I was thinking of the same issue. How can we build a tool to insert the nested namespace. My coworker said that it should be a matter of brace matching while ignoring string literals with braces. In other words, we might be able to get away with a simpler parser but probably much more than what regex can do.
Where searching for the ending curly braces is the challenge. I had some success experimenting with boost::expressive, but this code is far from ready. I am also a lot more concerned with parsing issues and robustness as I envision many "reasonable" ways to break the matching. So my suggestion is to start with the boost::regexp code which works well for the simpler use-case.
If anybody want to work on the nested namespace use-case, then I am happy to provide them with my code. I do however not have time to spend on this now, sorry.
That's so sad :( But thanks for the prototype. It can only help :)

Bjørn Roald wrote:
Well -namespace=boost_1_34_0 actually does a bit more. It renames directories and include directives so stuff like
#include <boost/filesystem/path.hpp>
becomes
#include <boost_1_34_0/filesystem/path.hpp>
and should work. It also replaces all instances of BOOST_ prefix in preprocessor symbol definitions and references with BOOST_1_34_0_ and processes Jamfiles as well so my hope was that all should build, test, and work without touch up work.
Also in the general case
../bin/boost/libs/filesystem/build/libboost_filesystem.a/gcc/debug/libboost_filesystem-gcc-d-1_33_1.a
should become
../bin/boost_1_34_0/libs/filesystem/build/lib/boost_1_34_0_filesystem.a/gcc/debug/libboost_1_34_0_filesystem-gcc-d-1_33_1.a
In this specific case the additional 1_34_0 substrings are just annoyance, so I guess we need to treat it as a special case when we have a version part of the new namespace. But as I said library path naming is not implemented yet. I guess this can be done by treating some configuration files for the build system. This may be outside the scope of the bcp tool.
So.. given all this, are you still sure this is worth adding?
Sure, why not.
Also, is -namespace the best name for the command line option?
Yep.
I have working code and will happily merge with 1.34.0 version of bcp and provide you a patch. Then you can use what you like, fix what you like to fix, and dump what you don't like :-)
OK :-) Cheers, John.

John Maddock wrote:
Bjørn Roald wrote:
Well -namespace=boost_1_34_0 actually does a bit more. It renames directories and include directives so stuff like
#include <boost/filesystem/path.hpp>
becomes
#include <boost_1_34_0/filesystem/path.hpp>
and should work. It also replaces all instances of BOOST_ prefix in preprocessor symbol definitions and references with BOOST_1_34_0_ and processes Jamfiles as well so my hope was that all should build, test, and work without touch up work.
Also in the general case
../bin/boost/libs/filesystem/build/libboost_filesystem.a/gcc/debug/libboost_filesystem-gcc-d-1_33_1.a
should become
../bin/boost_1_34_0/libs/filesystem/build/lib/boost_1_34_0_filesystem.a/gcc/debug/libboost_1_34_0_filesystem-gcc-d-1_33_1.a
In this specific case the additional 1_34_0 substrings are just annoyance, so I guess we need to treat it as a special case when we have a version part of the new namespace. But as I said library path naming is not implemented yet. I guess this can be done by treating some configuration files for the build system. This may be outside the scope of the bcp tool.
So.. given all this, are you still sure this is worth adding?
Sure, why not.
Also, is -namespace the best name for the command line option?
Yep.
I have working code and will happily merge with 1.34.0 version of bcp and provide you a patch. Then you can use what you like, fix what you like to fix, and dump what you don't like :-)
OK :-)
Cheers, John.
Ok, I will post a patch here within 2 or 3 days. -- Bjørn

Bjørn Roald wrote:
John Maddock wrote:
Bjørn Roald wrote:
I have working code and will happily merge with 1.34.0 version of bcp and provide you a patch. Then you can use what you like, fix what you like to fix, and dump what you don't like :-)
OK :-)
Cheers, John.
Ok, I will post a patch here within 2 or 3 days.
I am testing a version of my bcp code merged with main cvs version of bcp. It still has some rough edges and some TODOs, but it seems to be working well. I still want to test some more, and clean up the TODOs marked in the code. In addition I need to verify that I have not broken any features in bcp. I have only tested the new features I have added. But as of now, I hit the following problem while testing the new --namespace option: As I am testing against the regress tests in the 'status' directory of the target area after running with --cvs --namespace=ost. I am hitting a problem caused by bcp -cvs not copying all files in "libs/numeric/ublas/test/CVS/Entries" only these two files are copied: /concepts.cpp/1.2/Thu Feb 2 18:43:09 2006// /placement_new.cpp/1.8/Sun Jul 16 20:33:17 2006// ========= form of libs/numeric/ublas/test/CVS/Entries ========== ... /Jamfile/1.5/Thu Jun 8 20:40:17 2006/-ko/ /Jamfile.v2/1.7/Thu Jun 8 20:40:17 2006/-ko/ /README/1.2/Thu Jun 8 20:24:59 2006/-ko/ /concepts.cpp/1.2/Thu Feb 2 18:43:09 2006// /placement_new.cpp/1.8/Sun Jul 16 20:33:17 2006// /test1.cpp/1.1/Thu Jun 8 20:40:17 2006/-kk/ /test1.hpp/1.1/Thu Jun 8 20:40:17 2006/-kk/ /test11.cpp/1.2/Sun Jul 16 20:33:17 2006/-kk/ ... ======== from bcp/scan_cvs_tath.cpp ========= ... static const boost::regex file_expression("^(?:A\\s+)?/([^/\\n]+)/[^/\\n]*/[^/\\n]*/[^k/\\n]*(kb[^/\\n]*)?/[^/\\n]*"); ... Seems to me like the trouble is the "(kb[^/\\n]*)?" part of the regexp, which does not match "/-kk/" or "/-ko/" part of each entry. I am not familiar with the file format of the Entries file, so I wonder if this is intentional? As of the promised patch file; I am attaching a snapshot patch file of my merged bcp sources and one new source file. You can use these to see where I am heading. Any comments or suggestions are welcome. Remaining known issues: 1. Support custom file headers/footers to include in files treated with the --namespace option. Currently a hard coded text is used. 2. Fix logic so --unix-lines work in combination with --namespace and --diff-mode work when --namespace is not used 3. Special treatment, where necessary, of top level (BOOST_ROOT) jam files: project-root.jam, Jamfile.v2, boost-build.jam. In these files the general replace of the boost namespace is too general. 4. Exclusion of some source from treatment of the --namespace feature all together; e.g. too/jam/src. 5. most lines with ending braces of boost namespace in the boost sources seems to be marked with /* namespace boost */ or similar. Look into if this can be a simple way of locating them and support nested namespaces. 6. Verify that we have not broken standard bcp features. 7. Check validity of the change in add_path.cpp seems unrelated, but I could not find the code looking back a few versions in CVS. I guess I had added it to avoid annoying warnings from bcp and that it does the right thing? I will most likely not be able to work more on this before next weekend. -- Bjørn This file contains notes regarding enhansements to BOOST_ROOT/tools/bcp to support replacement of the boost namespace in sourcecode copied from the boost distrubution. ==================== HOWTO ====================== A new option: --namespace=ns set a namespace that will replace 'boost' is added to the bcp tool. This allow the user to specify a namespace to use as replacement for boost in exported source code. example use:
cd ~/src/boost mkdir -p /tmp/testbcp/mylib tools/bcp/run/bcp -cvs -namespace=mylib filesystem /tmp/testbcp/mylib
later you can use
tools/bcp/run/bcp -cvs --namespace=spoost --diff-only filesystem /tmp/testbcp/mylib
to only do the changes You can add boost-build.jam Jamfile Jamrules to the module-list on the bcp command line to get the top level build files needed to build the exported code. If you change the following 2 lines in the exported Jamfile [ glob-tree $(BOOST_ROOT)/boost/compatibility/cpp_c_headers : c* ] [ glob-tree $(BOOST_ROOT)/boost : *.hpp *.ipp *.h *.inc ] to [ glob-tree $(BOOST_ROOT)/mylib/compatibility/cpp_c_headers : c* ] [ glob-tree $(BOOST_ROOT)/mylib : *.hpp *.ipp *.h *.inc ] you can actually build the exported libraries with bjam. NOTE: see limitation regarding Boost.Build ================== TODO ========================= 1. Support replacing namespace boost with nested namespace, e.g. namespace boost ====> namespace mylib::boost or namespace boost ====> namespace mylib::stuff::platform 2. Better Boost.Build and Jamfile support in export =============== LIMITATIONS ====================== 1. Only replacement of boost namespace with single level namespace is suported. No replacement with nested namespace supported jet. 2. Boost.Build export not suported properly. Buildfiles are still configured for the "boost" name after export. E.g. libriaries built contain the string "boost" in their filenames. ? bjornr_notes.txt ? replace_namespace.cpp Index: Jamfile =================================================================== RCS file: /cvsroot/boost/boost/tools/bcp/Jamfile,v retrieving revision 1.3 diff -d -u -r1.3 Jamfile --- Jamfile 16 Jul 2006 11:31:52 -0000 1.3 +++ Jamfile 20 May 2007 14:01:50 -0000 @@ -5,7 +5,8 @@ subproject tools/bcp ; exe bcp - : add_path.cpp bcp_imp.cpp copy_path.cpp file_types.cpp + : add_path.cpp bcp_imp.cpp copy_path.cpp + replace_namespace.cpp file_types.cpp fileview.cpp main.cpp path_operations.cpp scan_cvs_path.cpp licence_info.cpp scan_licence.cpp output_licence_info.cpp <lib>../../libs/filesystem/build/boost_filesystem Index: Jamfile.v2 =================================================================== RCS file: /cvsroot/boost/boost/tools/bcp/Jamfile.v2,v retrieving revision 1.4 diff -d -u -r1.4 Jamfile.v2 --- Jamfile.v2 16 Jul 2006 11:31:52 -0000 1.4 +++ Jamfile.v2 20 May 2007 14:01:50 -0000 @@ -7,6 +7,7 @@ add_path.cpp bcp_imp.cpp copy_path.cpp file_types.cpp fileview.cpp main.cpp path_operations.cpp scan_cvs_path.cpp licence_info.cpp scan_licence.cpp output_licence_info.cpp + replace_namespace.cpp /boost/filesystem//boost_filesystem /boost/regex//boost_regex /boost/test//boost_prg_exec_monitor Index: add_path.cpp =================================================================== RCS file: /cvsroot/boost/boost/tools/bcp/add_path.cpp,v retrieving revision 1.12 diff -d -u -r1.12 add_path.cpp --- add_path.cpp 19 Feb 2006 16:19:02 -0000 1.12 +++ add_path.cpp 20 May 2007 14:01:50 -0000 @@ -123,6 +123,11 @@ // or we'll get an error: if(s.compare(0, 2, "./") == 0) s.erase(0, 2); + // + // if the name contain .html# , remove the html bookmark + // or we'll get warnings of none existing files + if(s.find(".html#") != std::string::npos) + s.erase(s.find(".html#")+5); if(s.find(':') == std::string::npos) { // only concatonate if it's a relative path Index: bcp.hpp =================================================================== RCS file: /cvsroot/boost/boost/tools/bcp/bcp.hpp,v retrieving revision 1.7 diff -d -u -r1.7 bcp.hpp --- bcp.hpp 10 Feb 2006 17:11:25 -0000 1.7 +++ bcp.hpp 20 May 2007 14:01:50 -0000 @@ -21,11 +21,13 @@ virtual void enable_summary_list_mode() = 0; virtual void enable_cvs_mode() = 0; virtual void enable_unix_lines() = 0; + virtual void enable_diff_only_mode() = 0; virtual void enable_scan_mode() = 0; virtual void enable_license_mode() = 0; virtual void enable_bsl_convert_mode() = 0; virtual void enable_bsl_summary_mode() = 0; virtual void set_boost_path(const char* p) = 0; + virtual void set_replacement_namespace(const char* ns) = 0; virtual void set_destination(const char* p) = 0; virtual void add_module(const char* p) = 0; Index: bcp_imp.cpp =================================================================== RCS file: /cvsroot/boost/boost/tools/bcp/bcp_imp.cpp,v retrieving revision 1.13 diff -d -u -r1.13 bcp_imp.cpp --- bcp_imp.cpp 10 Feb 2006 17:11:25 -0000 1.13 +++ bcp_imp.cpp 20 May 2007 14:01:51 -0000 @@ -71,6 +71,11 @@ m_unix_lines = true; } +void bcp_implementation::enable_diff_only_mode() +{ + m_diff_only_mode = true; +} + void bcp_implementation::set_boost_path(const char* p) { m_boost_path = fs::path(p, fs::native); Index: bcp_imp.hpp =================================================================== RCS file: /cvsroot/boost/boost/tools/bcp/bcp_imp.hpp,v retrieving revision 1.8 diff -d -u -r1.8 bcp_imp.hpp --- bcp_imp.hpp 10 Feb 2006 17:11:25 -0000 1.8 +++ bcp_imp.hpp 20 May 2007 14:01:51 -0000 @@ -52,11 +52,13 @@ void enable_summary_list_mode(); void enable_cvs_mode(); void enable_unix_lines(); + void enable_diff_only_mode(); void enable_scan_mode(); void enable_license_mode(); void enable_bsl_convert_mode(); void enable_bsl_summary_mode(); void set_boost_path(const char* p); + void set_replacement_namespace(const char* ns); void set_destination(const char* p); void add_module(const char* p); @@ -68,10 +70,15 @@ void add_directory(const fs::path& p); void add_file(const fs::path& p); void copy_path(const fs::path& p); + void replace_namespace(const fs::path& p); + fs::path destination_path(const fs::path p, std::string seperator = "/"); void add_file_dependencies(const fs::path& p, bool scanfile); bool is_source_file(const fs::path& p); bool is_html_file(const fs::path& p); bool is_binary_file(const fs::path& p); + bool is_bjam_file(const fs::path& p); + bool diff_only_copy( const std::string& new_file, + fs::path destination_path ); void add_dependent_lib(const std::string& libname, const fs::path& p); void create_path(const fs::path& p); // license code: @@ -84,6 +91,8 @@ bool m_license_mode; // generate license information for files listed bool m_cvs_mode; // check cvs for files bool m_unix_lines; // fix line endings + bool m_diff_only_mode; // diff only mode + bool m_replace_namespace; // replace the boost namespace bool m_scan_mode; // scan non-boost files. bool m_bsl_convert_mode; // try to convert to the BSL bool m_bsl_summary_mode; // summarise BSL issues only Index: copy_path.cpp =================================================================== RCS file: /cvsroot/boost/boost/tools/bcp/copy_path.cpp,v retrieving revision 1.4 diff -d -u -r1.4 copy_path.cpp --- copy_path.cpp 30 Nov 2003 13:02:14 -0000 1.4 +++ copy_path.cpp 20 May 2007 14:01:51 -0000 @@ -20,35 +20,50 @@ void bcp_implementation::copy_path(const fs::path& p) { assert(!fs::is_directory(m_boost_path / p)); - if(fs::exists(m_dest_path / p)) - { - std::cout << "Copying (and overwriting) file: " << p.string() << "\n"; - fs::remove(m_dest_path / p); - } - else - std::cout << "Copying file: " << p.string() << "\n"; + fs::path dest_p = destination_path( p ); + // // create the path to the new file if it doesn't already exist: // - create_path(p.branch_path()); + create_path(dest_p.branch_path()); // // do text based copy if requested: // - if(m_unix_lines && !is_binary_file(p)) + if(m_replace_namespace && (is_source_file(p) || is_bjam_file(p))) { - std::ifstream is((m_boost_path / p).native_file_string().c_str()); - std::istreambuf_iterator<char> isi(is); - std::istreambuf_iterator<char> end; - - std::ofstream os((m_dest_path / p).native_file_string().c_str(), std::ios_base::binary | std::ios_base::out); - std::ostreambuf_iterator<char> osi(os); - - std::copy(isi, end, osi); + replace_namespace(p); } else { - // binary copy: - fs::copy_file(m_boost_path / p, m_dest_path / p); + + if( fs::exists(m_dest_path / dest_p)) + { + // TODO: need some support for diff mode here, for now + // diff_mode is only supported within replace_namespace(p); + + std::cout << "Copying (and overwriting) file: " << p.string() << "\n"; + fs::remove(m_dest_path / dest_p ); + } + else + std::cout << "Copying file: " << p.string() << "\n"; + + if(m_unix_lines && !is_binary_file(p)) + { + std::ifstream is((m_boost_path / p).native_file_string().c_str()); + std::istreambuf_iterator<char> isi(is); + std::istreambuf_iterator<char> end; + + std::ofstream os( ( m_dest_path / dest_p ).native_file_string().c_str(), + std::ios_base::binary | std::ios_base::out); + std::ostreambuf_iterator<char> osi(os); + + std::copy(isi, end, osi); + } + else + { + // binary copy: + fs::copy_file(m_boost_path / p, m_dest_path / dest_p); + } } } Index: file_types.cpp =================================================================== RCS file: /cvsroot/boost/boost/tools/bcp/file_types.cpp,v retrieving revision 1.6 diff -d -u -r1.6 file_types.cpp --- file_types.cpp 3 Nov 2005 12:12:37 -0000 1.6 +++ file_types.cpp 20 May 2007 14:01:51 -0000 @@ -9,6 +9,7 @@ * void bcp_implementation::is_source_file(const fs::path& p) * void bcp_implementation::is_html_file(const fs::path& p) * void bcp_implementation::is_binary_file(const fs::path& p) + * void bcp_implementation::is_bjam_file(const fs::path& p) */ #include "bcp_imp.hpp" @@ -19,8 +20,8 @@ static const boost::regex e( ".*\\." "(?:" - "c|cxx|h|hxx|inc|.?pp|yy?" - ")", + "c|cxx|h|hxx|inl|inc|.?pp|yy?" + ")", boost::regex::perl | boost::regex::icase ); return boost::regex_match(p.string(), e); @@ -45,13 +46,27 @@ if(pos != m_cvs_paths.end()) return pos->second; } static const boost::regex e( - ".*\\." + "(.*\\." "(?:" "c|cxx|cpp|h|hxx|hpp|inc|html?|css|mak|in" ")" "|" - "(Jamfile|makefile|configure)", + "(Jamfile|Jamfile\\.v2|Jamrules|Jambase|makefile|configure)", boost::regex::perl | boost::regex::icase); return !boost::regex_match(p.leaf(), e); } + +bool bcp_implementation::is_bjam_file(const fs::path& p) +{ + static const boost::regex e( + ".*\\." + "(?:" + "jam" + ")" + "|" + "(Jamfile|Jamfile\\.v2|Jamrules|Jambase)", + boost::regex::perl | boost::regex::icase); + return boost::regex_match(p.leaf(), e); + +} Index: main.cpp =================================================================== RCS file: /cvsroot/boost/boost/tools/bcp/main.cpp,v retrieving revision 1.8 diff -d -u -r1.8 main.cpp --- main.cpp 10 Feb 2006 17:11:25 -0000 1.8 +++ main.cpp 20 May 2007 14:01:51 -0000 @@ -33,10 +33,12 @@ "\n" "Options:\n" " --boost=path sets the location of the boost tree to path\n" - " --scan treat the module list as a list of (possibly non-boost)\n" + " --namespace=ns set namespace 'ns' that will replace 'boost'\n" + " --scan treat the module list as a list of (possibly non-boost)\n" " files to scan for boost dependencies\n" " --cvs only copy files under cvs version control\n" " --unix-lines make sure that all copied files use Unix style line endings\n" + " --diff-only only overwrite files when new file differs from existing original\n" "\n" "module-list: a list of boost files or library names to copy\n" "html-file: the name of a html file to which the report will be written\n" @@ -123,12 +125,21 @@ { papp->enable_unix_lines(); } + else if(0 == std::strcmp("--diff-only", argv[i])) + { + papp->enable_diff_only_mode(); + } else if(0 == std::strncmp("--boost=", argv[i], 8)) { papp->set_boost_path(argv[i] + 8); } + else if(0 == std::strncmp("--namespace=", argv[i], 12)) + { + papp->set_replacement_namespace(argv[i] + 12); + } else if(argv[i][0] == '-') { + std::cout << "wrong argument: " << argv[i] << std::endl; show_usage(); return 1; } Index: scan_cvs_path.cpp =================================================================== RCS file: /cvsroot/boost/boost/tools/bcp/scan_cvs_path.cpp,v retrieving revision 1.4 diff -d -u -r1.4 scan_cvs_path.cpp --- scan_cvs_path.cpp 3 Nov 2005 12:12:37 -0000 1.4 +++ scan_cvs_path.cpp 20 May 2007 14:01:51 -0000 @@ -29,7 +29,7 @@ static const boost::regex dir_expression("^(?:A\\s+)?D/([^/\\n]+)/"); static const int file_subs[] = {1,2,}; - for(int entry = 0; entry < sizeof(file_list)/sizeof(file_list[0]); ++entry) + for(unsigned int entry = 0; entry < sizeof(file_list)/sizeof(file_list[0]); ++entry) { fs::path entries(m_boost_path / p / file_list[entry]); if(fs::exists(entries))

Bjørn Roald wrote:
If you intent to test the bcp approach, my code is a good way to start. I looked it up, and I have several versions of it available. The latests version introduces boost::expressive as dependency to bcp to handle changing boost to nested namespaces like boost::1.34.0. This
Our programmers perfer this nested namespace style so I am definitely interested in bcp if it can do it.
code is as far as I remember not complete, so the best start would be the earlier version based on boost::regexp only. If this is what you want to try, I will send you the code.
Does this mean that the regexp based code cannot do nested namespaces? If so, is it possible to keep the dependency on regexp and yet still have nested namespaces or is the expressive dependency a must?
I did a quick diff against a resent cvs version of boost, and it seems that there are a few changes to bcp we want to merge in. If you are interested and give me a few days, I will find time to merge and test the merged version a bit for you.
Either way, I can test it before I spam the facility :) So yes, I can test bcp.
If a bcp based solution becomes useful, I think it should be considered to be included in the tool in the boost distribution. As of now I think we need some more prof of the usefulness of the concept, and I think documentation is needed as to the best ways to use such a tool. Maybe some conventions can be introduced for naming and how the tool is used in the configuration management. Here is a simple sketch of my ideas:
boost.org | my_place.com ------------+--------------------------+-------------------------------- central SCM | local CM/SCM | build environment ------------+--------------------------+-------------------------------- repo <--svn--> namespace boost | --bcp-> namespace mylib_boost | | | do your boost work here | bcp used as a build step only As the figure illustrate, bcp is a one way tool when you change the namespace. I feel strongly that the tool shall be used as a build step rather than keeping the modified code under local source control. The better way is to do modifications, patches, testing and contributions for boost as other boost users. Then use bcp as a build-time tool.
I see your point but at least in our facility there is only a handful of developers that are interested in contributing to boost. The rest are users (only a handful also as I am actively trying to evangelize users). I am leaning towards permanently changing the namespaces to our facility for regular users and for those interested can still download boost sources and contribute directly.
To reduce overhead introduced by this build-time, I added an option to my bcp version which in case the target file exist only will overwrite if file content changes. This reduces build time dramatically for changes after initial build.
Help is offered ;-) Anyone ready to try this can mail me off the list if you prefer.
BTW, if you want to take this off-line, feel free to email me directly. I am ready to test it when you are :) Thanks!!

Piyo wrote:
Bjørn Roald wrote:
If you intent to test the bcp approach, my code is a good way to start. I looked it up, and I have several versions of it available. The latests version introduces boost::expressive as dependency to bcp to handle changing boost to nested namespaces like boost::1.34.0. This
Our programmers perfer this nested namespace style so I am definitely interested in bcp if it can do it.
In that case you need to look at a different version of my code than the one I suggested for John Maddock to include in bcp. Also you have to expect some work to get it usable, but I am convinced it can be done.
code is as far as I remember not complete, so the best start would be the earlier version based on boost::regexp only. If this is what you want to try, I will send you the code.
Does this mean that the regexp based code cannot do nested namespaces?
Assuming my knowledges of regexp is correct, this is tricky and may not be possible in a robust way at all.
If so, is it possible to keep the dependency on regexp and yet still have nested namespaces or is the expressive dependency a must?
expressive is not a must, only a possible solution. And in any case, we are only talking about the bcp code anyway, not your code. John Maddock may prefer a different solution than xpressive if this feature ever reaches the standard version of the bcp.
I did a quick diff against a resent cvs version of boost, and it seems that there are a few changes to bcp we want to merge in. If you are interested and give me a few days, I will find time to merge and test the merged version a bit for you.
Either way, I can test it before I spam the facility :) So yes, I can test bcp.
If you want to have nested namespace support. Then more than testing is needed from you. A working implementation is needed.
If a bcp based solution becomes useful, I think it should be considered to be included in the tool in the boost distribution. As of now I think we need some more prof of the usefulness of the concept, and I think documentation is needed as to the best ways to use such a tool. Maybe some conventions can be introduced for naming and how the tool is used in the configuration management. Here is a simple sketch of my ideas:
boost.org | my_place.com ------------+--------------------------+-------------------------------- central SCM | local CM/SCM | build environment ------------+--------------------------+-------------------------------- repo <--svn--> namespace boost | --bcp-> namespace mylib_boost | | | do your boost work here | bcp used as a build step only As the figure illustrate, bcp is a one way tool when you change the namespace. I feel strongly that the tool shall be used as a build step rather than keeping the modified code under local source control. The better way is to do modifications, patches, testing and contributions for boost as other boost users. Then use bcp as a build-time tool.
I see your point but at least in our facility there is only a handful of developers that are interested in contributing to boost. The rest are users (only a handful also as I am actively trying to evangelize users). I am leaning towards permanently changing the namespaces to our facility for regular users and for those interested can still download boost sources and contribute directly.
I know this may seem complicated to set up, but consider writing test cases, reporting bugs, testing patches, and contributing to boost as something that becomes natural with one setup, and very awkward with the other.
To reduce overhead introduced by this build-time, I added an option to my bcp version which in case the target file exist only will overwrite if file content changes. This reduces build time dramatically for changes after initial build.
Help is offered ;-) Anyone ready to try this can mail me off the list if you prefer.
BTW, if you want to take this off-line, feel free to email me directly.
No need from my side, but at some point we are probably only spamming the list ;-)
I am ready to test it when you are :)
Let me sort out where John want to go first, then I will be back to you. If you want the code as is and start working yourselves, then it is one mail away. I do not want to post anything to the list or elsewhere before it is merged and tested with 1.34.0. And that includes testing with Boost.Build v2. -- Bjørn

Bjørn Roald wrote:
Piyo wrote:
I am open to any form of solution whether it be macro-based or bcp
If you intent to test the bcp approach, my code is a good way to start. [snip]
Oh and my coworker also brought up this issue. Is that handled by bcp?? ------------------------------------------------------------------------ Also, the issue of cpp guards needs to be raised also, like in aligned_storage.hpp: #ifndef BOOST_ALIGNED_STORAGE_HPP #define BOOST_ALIGNED_STORAGE_HPP You would want to be able to include multiple versions of boosts in the same compilation unit.

Piyo wrote:
Bjørn Roald wrote:
Piyo wrote:
I am open to any form of solution whether it be macro-based or bcp
If you intent to test the bcp approach, my code is a good way to start.
[snip]
Oh and my coworker also brought up this issue. Is that handled by bcp??
------------------------------------------------------------------------ Also, the issue of cpp guards needs to be raised also, like in aligned_storage.hpp:
#ifndef BOOST_ALIGNED_STORAGE_HPP #define BOOST_ALIGNED_STORAGE_HPP
becomes: #ifndef BOOST_1_33_1_ALIGNED_STORAGE_HPP #define BOOST_1_33_1_ALIGNED_STORAGE_HPP or whatever other upercase string prefix is derived from your target namespace. So yes, it is handled.
You would want to be able to include multiple versions of boosts in the same compilation unit.
yes. -- Bjørn

on Wed May 16 2007, Piyo <cybermax_69-AT-yahoo.com> wrote:
I am open to any form of solution whether it be macro-based or bcp or Xerces style. I just find it sad that I am stopping 1.34 from entering our company on purpose because I do not think that adding to our existing 1.32 and 1.33.1 installations will help. Is there anyway we can get the core boost developers to look into this?
It's an important problem (it's been raised several times), but that said, what do you think the core boost developers can "look into" that you can't? Most of the core devels are probably not as familiar with the problem as you are. My suggestion is that you write up a concrete proposal for what we should do, what the consequences are, and how it should be used, and post it to this list. We'll discuss it. Once semi-stable it should go on a wiki, and we'll have to decide if it should be adopted. There are many different tools you could use to approach this, in various combinations, with different implications: namespace aliases macros using declarations using directives nested namespaces I'd want to see that you had considered all the viable permutations and chosen the best one.
Since I am the only boost-evangelist here, I can't imagine me stopping progress over something like this.
HELP!! :)
What can we do to help? -- Dave Abrahams Boost Consulting http://www.boost-consulting.com
participants (8)
-
Bjørn Roald
-
David Abrahams
-
Felipe Magno de Almeida
-
Jody Hagins
-
John Maddock
-
Marcus Lindblom
-
Piyo
-
Ulrich Eckhardt