What are the dusty corners a newcomer to CMake will want to know? - build

I've done a lot of projects and a lot of different build systems and CI tools. Most recently, I've been exposed to the occasionally challenging task of adding to an autotools based environment for a reasonably sized C++ application. While I love the ease of use for the end user, I'm not so fond of dealing with m4 and all of the auto* tools from the developer side.
I'm working on a reasonably large side project in my spare time and have decided that I'd like to take CMake for a test drive. Since I'm just starting out, I'm obviously planning on digging through the documentation, FAQ, wikis, etc. and learning by doing. BTW, I'd fork over money for the "Mastering CMake" book, but the comments that I found on Amazon were enough to make me decide that it probably isn't worth the money. All that being said, in anything new there are often "gotchas" that a newcomer will often stumble over that the old pros have long since learned to avoid. I'm wondering what those are based on people's experience with CMake, and I'm hoping to cut my learning pains down a bit by asking here.
I should point out that I'm planning on building on Linux primarily, and other UN*X variants. Windows isn't really a concern from my POV. This is a large server-side application, with a Web and CLI interface for operators, a northbound REST interface for automation/integration with OSS tooling, and a SOAP southbound interface for CPEs. I'm going to need a lot of third-party libraries and applications to get this all to work unless I want to take the next 10 years to build this all by hand. :)

First of all, I think CMake is an excellent build-tool. It has in my opinion by far the best support for multi-platform-builds and has a powerful mechanism for finding 3rd-party libraries. Combined with CPack it even provides reasonable options for packaging and installation. Some hints and possible problems:
Always aim for out-of-source builds: An obvious one: It can be difficult for a project designed for in-source builds to get it built out-of-source. So, if you can design it from the start, aim for out-of-source builds.
Syntax: The syntax can be bizarre with a lot of strange quirks, although this is getting better since the 2.6 and 2.8 versions.
Caching of variables. CMake keeps track of a cache of variables and settings and sometimes this can be a problem when rebuilding something. Try to remove the CMakeCache.txt (or clean your out-of-source build-directory) and rebuild. This one is also mentioned by DarenW.
Finding and configuring 3rd-party libraries: If you have a large project depending on several libraries (your own or 3rd-party), this will likely be the most troublesome.
I seriously recommend diving into the find_package command. It is very powerful, but fully depends on the quality of the FindXXX.cmake files. Especially when building on multiple platforms (mostly Windows and Mac), these files may require some tweaking or you may need to write your own.
Problems with linking libraries like debugging "undefined references" or mismatches between debug|release or static|shared can be difficult to debug. Especially with 3rd-party libraries these problems may be caused by incorrect 3rd-party FindXXX.cmake files...

One thing that took me a while to figure out: if anything goes haywire with a build because I've changed a compiler option, added/removed a .c file, had confusion with support library versions, etc. - it's best to delete the build tree and run CMake from scratch. Otherwise, even though CMake and make seem to do their job, the resulting executable crashes.
It could be we're "not doing it right", but that's becoming a common problem with many tools, languages, frameworks, etc. It's not possible for everyone to be expert at all the software they depend on, and "not doing it right" is the only way a lot of things get done even among professionals. CMake is pretty smart about a lot of things, but not everything. Nuking the build tree remains a commonly used technique on our project.

Related

The cmake quandary

I am working on a C++ project. It is not much complicated so far, yet depends on a bunch of "popular" libraries (nlohmann/json, ToruNiina/toml11 just to name a few). All of them have some CMakeLists.txt and from my not-that-experienced point of view, I consider them well structured.
Now of course I can compile the libraries one by one, or include a "copy" into my project repo, but I want to be better than that. After researching about available build tools, I have decided to use cmake to build and manage a C++ project. The promise was to get a stable, widely supported tool that will help to simplify & unify the build process. Moreover, from the project nature I have no privilege to impose any requirements on the target machine; I need to pack everything for the deployment.
I have spent several days reading, watching and testing out various cmake tutorials, handbooks and manuals. I have to admit, I quickly started to feel that a tool that is supposed to clarify development process keeps introducing new obscurities contrary to its purpose. Originally, I credited this to my lack of experience, yet...
I read articles about why not to bundle dependencies, only to be followed by methods of doing so. I have found recommendation to use one way A over B, C over B and later A over C. It took me a while to figure out the differences between 2.8 and 3.0, the obscurity of target_link_libraries, setting cxx version and/or compiler warning flags and so on.
My point is that even after an exhausting expedition into the seas of cmake, I am still not sure about some elementary questions:
How is cmake meant to be used?
What is a standard, what is a courtesy, and what is none of those?
How can I tell that something is a feature, an archaic backwards compatibility, or both?
Now I will illustrate this on my project. I only need something like this
cmake_minimum_required(VERSION 3.14)
project(CaseCore CXX)
add_executable(myBinary list/of/cpp/sources.cpp)
target_link_libraries(myBinary PUBLIC someExternalLibs likeForExample nlohmann_json::nlohmann_json oqs)
The only problem is with the libraries (there is no space for other problems anyway). I want to build them with the project and dont want to make a local copy (not to drag ton of unrelated files all along). First, I created forks of library repos in order to have a reliable source and be able to merge newer versions into my fork.
Now the decision was whether to use git submodule or some other scheme, I've read submodule doesnt perform that well and also preferred the whole thing to be managed by cmake alone. I started with ExternalProject_Add but later I found about FetchContent which helped me to add the external dependencies easily into my cmake list
FetchContent_Declare(nlohmann
GIT_REPOSITORY https://github.com/my-reliable-fork-of/json
GIT_TAG v3.7.3
)
message(STATUS "Fetching Json...this may take a while")
FetchContent_MakeAvailable(nlohmann)
Seems and works well and minimal. However, I always have to search the library itself in order to find/guess which targets to link to my executable target? There seems to be close to zero convention and unless the respective CMakeLists.txt is simple enough to read it, I tend to guess the target names until i find it.
Recently I wanted to link liboqs from here and the abovementioned scenario did not really help; for some reason, I can link oqs, #include "oqs/oqs.h", yet the dynamic library is not created and execution terminates. I am pretty sure I can resolve the problem after another span of time spent googling and playing around with various cmake variables. Yet this is not the way that I expected cmake to help me manage my project; it is actually quite the opposite.
Just to be clear, I turned down other methods including
add_subdirectory from local repo copy (git submodule)
ExternalProject_Add from local repo copy (git submodule)
ExternalProject_Add from online repo
find_package
as they seemed to be much more obscure/old-style etc (eventhough despite hours of researching, they all still seem as pretty much as just many ways to do the same thing to me)
Now that I have
Am I doing something wrong, or is it really what working with cmake should look like?
Do I really have to "reverse-engineer" other people's CMakeLists in order to use a library?
Under these circumstances, how can I convince my coworkers to use similar work process?
and finally
How can I adjust my work in order to ease these difficulities for others?
I love C++ the more I use it. Yet I spend a tremendous amount of my productive time on solving dependencies...and I do not want to make this guy even more angry.
How is cmake meant to be used?
The typical cmake usage matches the old autotools usage:
$ cmake /path/to/src #replaces /path/to/src/configure
$ make
$ make install
Some targets changed (e.g., make check vs make test), and cmake doesn't provide all the same standard targets (e.g., make distclean), but the usage I have above is what most developers will do (and since cmake re-runs itself, it's really just the second step most of the time).
If your CMakeLists.txt doesn't support this workflow, you should have a very good reason. Most tooling will assume a workflow like this, so you're severely limiting yourself.
What is a standard, what is a courtesy, and what is none of those?
Outside of the above, cmake is pretty much the wild west. Things are becoming more standardized thanks to better documentation and training, but it's far from perfect.
A well-behaved cmake project should export its targets (lots of questions and answers on Stack Overflow about this) and propagate flags and dependencies. This makes it far easier for dependent projects to consume exported targets, and luckily it's easy to do.
How can I tell that something is a feature, an archaic backwards compatibility, or both?
There's nothing I'm aware of that makes these distinctions. In general, newer methods leverage the target_* functions instead of the global ones (e.g., target_include_directories vs include_directories). The target_* functions are also used to propagate flags, include directories, compiler features, and dependent libraries like I mentioned above.
Am I doing something wrong, or is it really what working with cmake should look like?
You're talking about managing external dependencies, and I'm going to skip this to avoid getting into opinions. The short version is that C and C++ dependencies are hard, and there's many competing ways of managing them in a project. They each have pros and cons, but most are still designed for the authors' use cases. You'll have to figure out what use cases you need, and choose tools and workflows based on that.
Do I really have to "reverse-engineer" other people's CMakeLists in order to use a library?
A well-behaved cmake project will export its targets properly, even if they use different dependency management than you do. If they don't, send the project a pull request (exporting isn't hard, and it's good to learn how) or just file bugs against them, especially if they're already using cmake as a build system.
Under these circumstances, how can I convince my coworkers to use similar work process?
It depends on your coworkers, and mileage will vary. I've dealt with coworkers who want to embrace best practices and support flexibility, and I've dealt with coworkers who are content only doing enough to solve the problems we're facing right now.

Are multiple build tools (qmake + scons) on the same project considered bad practice?

My program depends on two libraries. The first one uses scons and the last one uses qmake. The program itself uses scons. So to build the whole project, I have a makefile that builds the first library with scons and the second library with qmake.
Is it considered bad practice to use multiple build tools on the same project? Should I create a scons-file to build the last library too?
I would prefer not over-complicating things if its not necessary. This is often referred to as KISS.
SCons can do both normal compilations and Qt compilations. Likewise with Qt (qmake).
My personal preference would be to use SCons for both. To build Qt with SCons, refer to the qt4tools, or the normal SCons qt tool, mentioned here.
I wouldn't say so. On complex projects it's common for source to come from several different source control systems (I think I read somewhere that the Chromium project references something like 7 different repositories). Projects grow and have quirks of history that mean different parts may be grafted on over time, contributed by different people with different backgrounds.
If it looks like it might be trivial to convert the project to use scons then do that. If it looks any more complicated than that then it may be worth sticking with what you've got. If you find that maintaining the qmake system becomes a time-sink then it may be worth investing the time in migrating to scons.
Think of it in economic terms: if it's not taking time or effort to maintain then leave it as is. If it is taking time to maintain, consider if it would actually take longer to migrate to something else. Don't forget the xkcd classic:

C++ Libraries ecosystem using CMake and Ryppl

I am interested in building a cross-platform C++ Library and distributing it in source form. I want the consumers of this library to be able to acquire it, build it and consume it inside their software very easily on whatever platform they are working on and for whatever platform they are targeting. At the same time while building my library, I also want to be able to consume other popular OSS libraries through a similar mechanism.
I see that CMake and Ryppl were created with these intentions in mind and to some extent they do solve some of these problems, especially the build problem. But I don't quite know how exactly to go about achieving the above mentioned goals. Is it OK to settle on CMake as the build solution? How do I solve the library acquisition and distribution problem? Simply host the sources somewhere and let people discover, download and build them? Or is there a better way?
At the time of writing there is no accepted solution that handles everything you want. CMake gives you cross-platform builds and git (with submodules) gives you a way to manage source-level dependencies if all other projects are using CMake. But, in practice, many common dependencies you project wil need don't use CMake, or even Git.
Ryppl is meant to solve this, but progress is slow because the challenge is such a hard one.
Arguably, the most successful solution so far is to write header-only libraries. This is common practice and means your users just include your files, and their build system of choice takes care of everthing. There are no binary dependencies to manage.
TheHouse's answer is still essentially true. Also there don't seem to have been any updates to ryppl itself for a while (3 years) and the ryppl.org domain has expired.
There are some new projects aiming to solve the packaging issue.
Both build2 and wrap from mesonbuild have that goal in mind.
A proposal was made recently to add packages to the c++ standard which may open up the debate (reddit discussion here).
Wrap looks promising as meson's author has learned from cmake.
There is a good video when its author discussing this here.
build2 seems more oblivious (and therefore condemned to reinvent). However both suffer from trying to solve the external project dependencies issue simultaneously with providing a complete build system.
conan.io is another recent attempt which doesn't try to provide the build system as well. Time will tell if any of these gain any traction.
The accepted standard for packaging C and C++ projects on Unix was always a source tarball + a configure script (autotools) + make.
cmake is now beginning to replace autotools as your first choice.
It is able create RPMs and tarballs for distribution purposes.
Its also worth considering the package managers built into the various flavours of Linux. The easiest to build and install projects are those where most of the dependencies can be pulled in via yum or apt. This won't help you on windows of course. While there is a high barrier to entry getting your own projects added to the main Linux repositories (e.g. RedHat, Debian) there is nothing to stop you adding your maintaining your own satellite repo.
The difference between that and just hosting your project on github or similar is you can provide pre-built binaries for a number of popular systems.
You might also consider that configure times checks (e.g. from cmake findLibrary()) and your own documentation will tell people what needs to be installed as a prerequisite and providing you don't make it too onerous that might be enough.

What are the differences between Autotools, Cmake and Scons?

What are the differences between Autotools, Cmake and Scons?
In truth, Autotools' only real 'saving grace' is that it is what all the GNU projects are largely using.
Issues with Autotools:
Truly ARCANE m4 macro syntax combined with verbose, twisted shell scripting for tests for "compatibility", etc.
If you're not paying attention, you will mess up cross-compilation ability (It
should clearly be noted that Nokia came up with Scratchbox/Scratchbox2 to side-step highly broken Autotools build setups for Maemo/Meego.) If you, for any reason, have fixed, static paths in your tests, you're going to break cross-compile support because it won't honor your sysroot specification and it'll pull stuff from out of your host system. If you break cross-compile support, it renders your code unusable for things like
OpenEmbedded and makes it "fun" for distributions trying to build their releases on a cross-compiler instead of on target.
Does a HUGE amount of testing for problems with ancient, broken compilers that NOBODY currently uses with pretty much anything production in this day and age. Unless you're building something like glibc, libstdc++, or GCC on a truly ancient version of Solaris, AIX, or the like, the tests are a waste of time and are a source for many, many potential breakages of things like mentioned above.
It is pretty much a painful experience to get an Autotools setup to build usable code for a Windows system. (While I've little use for Windows, it is a serious concern if you're developing purportedly cross-platform code.)
When it breaks, you're going to spend HOURS chasing your tail trying to sort out the things that whomever wrote the scripting got wrong to sort out your build (In fact, this is what I'm trying to do (or, rather, rip out Autotools completely- I doubt there's enough time in the rest of this month to sort the mess out...) for work right now as I'm typing this. Apache Thrift has one of those BROKEN build systems that won't cross-compile.)
The "normal" users are actually NOT going to just do "./configure; make"- for many things, they're going to be pulling a package provided by someone, like out of a PPA, or their distribution vendor. "Normal" users aren't devs and aren't grabbing tarballs in many cases. That's snobbery on everyone's part for presuming that is going to be the case there. The typical users for tarballs are devs doing things, so they're going to get slammed with the brokenness if it's there.
It works...most of the time...is all you can say about Autotools. It's a system that solves several problems that only really concerns the GNU project...for their base, core toolchain code. (Edit (05/24/2014): It should be noted that this type of concern is a potentially BAD thing to be worrying about- Heartbleed partially stemmed from this thinking and with correct, modern systems, you really don't have any business dealing with much of what Autotools corrects for. GNU probably needs to do a cruft removal of the codebase, in light of what happened with Heartbleed) You can use it to do your project and it might work nicely for a smallish project that you don't expect to work anywhere except Linux or where the GNU toolchain is clearly working correctly on. The statement that it "integrates nicely with Linux" is quite the bold statement and quite incorrect. It integrates with the GNU toolsuite reasonably well and solves problems that IT has with it's goals.
This is not to say that there's no problems with the other options discussed in the thread here.
SCons is more of a replacement for Make/GMake/etc. and looks pretty nice, all things considered However...
It is still really more of a POSIX only tool. You could probably more easily get MinGW to build Windows stuff with this than with Autotools, but it's still really more geared to doing POSIX stuff and you'd need to install Python and SCons to use it.
It has issues doing cross-compilation unless you're using something like Scratchbox2.
Admittedly slower and less stable than CMake from their own comparison. They come up with half-hearted (the POSIX side needs make/gmake to build...) negatives for CMake compared to SCons. (As an aside, if you're needing THAT much extensibility over other solutions, you should be asking yourself whether your project's too complicated...)
The examples given for CMake in this thread are a bit bogus.
However...
You will need to learn a new language.
There's counter-intuitive things if you're used to Make, SCons, or Autotools.
You'll need to install CMake on the system you're building for.
You'll need a solid C++ compiler if you don't have pre-built binaries for it.
In truth, your goals should dictate what you choose here.
Do you need to deal with a LOT of broken toolchains to produce a valid working binary? If yes, you may want to consider Autotools, being aware of the drawbacks I mentioned above. CMake can cope with a lot of this, but it worries less with it than Autotools does. SCons can be extended to worry about it, but it's not an out-of-box answer there.
Do you have a need to worry about Windows targets? If so, Autotools should be quite literally out of the running. If so, SCons may/may not be a good choice. If so, CMake's a solid choice.
Do you have a need to worry about cross-compilation (Universal apps/libraries, things like Google Protobufs, Apache Thrift, etc. SHOULD care about this...)? If so, Autotools might work for you so long as you don't need to worry about Windows, but you're going to spend lots of time maintaining your configuration system as things change on you. SCons is almost a no-go right at the moment unless you're using Scratchbox2- it really doesn't have a handle on cross-compilation and you're going to need to use that extensibility and maintain it much in the same manner as you will with Automake. If so, you may want to consider CMake since it supports cross-compilation without as many of the worries about leaking out of the sandbox and will work with/without something like Scratchbox2 and integrates nicely with things like OpenEmbedded.
There is a reason many, many projects are ditching qmake, Autotools, etc. and moving over to CMake. So far, I can cleanly expect a CMake based project to either drop into a cross-compile situation or onto a VisualStudio setup or only need a small amount of clean up because the project didn't account for Windows-only or OSX-only parts to the codebase. I can't really expect that out of an SCons based project- and I fully expect 1/3rd or more Autotools projects to have gotten SOMETHING wrong that precludes it building right on any context except the host building one or a Scratchbox2 one.
An important distinction must be made between who uses the tools. Cmake is a tool that must be used by the user when building the software. The autotools are used to generate a distribution tarball that can be used to build the software using only the standard tools available on any SuS compliant system. In other words, if you are installing software from a tarball that was built using the autotools, you are not using the autotools. On the other hand, if you are installing software that uses Cmake, then you are using Cmake and must have it installed to build the software.
The great majority of users do not need to have the autotools installed on their box. Historically, much confusion has been caused because many developers distribute malformed tarballs that force the user to run autoconf to regenerate the configure script, and this is a packaging error. More confusion has been caused by the fact that most major linux distributions install multiple versions of the autotools, when they should not be installing any of them by default. Even more confusion is caused by developers attempting to use a version control system (eg cvs, git, svn) to distribute their software rather than building tarballs.
It's not about GNU coding standards.
The current benefits of autotools — specifically when used with automake — is that they integrate very well with building Linux distribution.
With cmake for example, it's always "was it -DCMAKE_CFLAGS or -DCMAKE_C_FLAGS that I need?" No, it's neither, it's "-DCMAKE_C_FLAGS_RELEASE". Or -DCMAKE_C_FLAGS_DEBUG. It's confusing - in autoconf, it's just ./configure CFLAGS="-O0 -ggdb3" and you have it.
In integration with build infrastructures, scons has the problem that you cannot use make %{?_smp_mflags}, _smp_mflags in this case being an RPM macro that roughly expands to (admin may set it) system power. People put things like -jNCPUS here through their environment. With scons that's not working, so the packages using scons may only get serialed built in distros.
What is important to know about the Autotools is that they are not a general build system - they implement the GNU coding standards and nothing else. If you want to make a package that follows all the GNU standards, then Autotools are an excellent tool for the job. If you don't, then you should use Scons or CMake. (For example, see this question.) This common misunderstanding is where most of the frustration with Autotools comes from.
While from a developers point of view, cmake is currently the most easy to use, from a user perspective autotools have one big advantage
autotools generate a single file configure script and all files to generate it are shipped with the distribution. it is easy to understand and fix with help of grep/sed/awk/vi. Compare this to Cmake where a lot of files are found in /usr/share/cmak*/Modules, which can't be fixed by the user unless he has admin access.
So, if something does not quite work, it can usually easily be "fixed" by using Standard Unix tools (grep/sed/awk/vi etc.) in a sledgehammer way without having to understand the buildsystem.
Have you ever digged through your cmake build directory to find out what is wrong? Compared to the simple shellscript which can be read from top to bottom, following the generated Cmake files to find out what is going on is quite difficult. ALso, with CMake, adapting the FindFoo.cmake files requires not only knowledge of the CMake language, but also might require superuser privileges.

Simultaneous C++ development on Linux and Windows

We have a handful of developers working on a non-commercial (read: just for fun)
cross-platform C++ project. We've already identified all the cross-platform libraries we'll need. However, some of our developers prefer to use Microsoft Visual C++ 2008, others prefer to code in Emacs on GNU/Linux. We're wondering if it is possible for all of us to work more or less simultaneously out of both environments, from the same code repository. Ultimately we want the project to compile cleanly on both platforms from the start.
Any of our developers are happily willing to switch over to the other environment if this is not possible. We all use both Linux and Windows on a regular basis and enjoy both, so this isn't a question of trying to educate one set devs about the virtues of the other platform. This is about each of us being able to develop in the environment we enjoy most yet still collaborate on a fun project.
Any suggestions or experiences to share?
Use CMake to manage your build files.
This will let you setup a single repository, with one set of text files in it. Each dev can then run the appropriate cmake scripts to build the correct build environment for their system (Visual Studio 2008/2005/GNU C++ build scripts/etc).
There are many advantages here:
Each dev can use their own build environment
Dependencies can be handled very cleanly, including platform specific deps.
Builds can be out of source, which helps prevent accidentally committing inappropriate files
Easy migration to new dev. environments (ie: when VS 2010 is released, some devs can migrate there just by rebuilding their build folder)
I've done it and seen it done without too many problems.
You'll want to try an isolate the code that is different for the different platforms. Also, you'll want to think about your directory structure. Something like
project/src <- .cc and .h files
project/src/linux|win <- code that is specific to one platform or the other
project/linux <- make files and other project related stuff
project/win <- .sln and .csproj files
Basically you just want to be really clear what is specific to each system and what is common.
Also, unit tests are going to be really important since there may be minor difference and you want to make it easy for the windows guys to run some tests to make sure the linux code works as expected and the other way around.
as mentioned in previous posts, Qt is a very easy method to do real simultaneous multi-platform development - independent of your IDE and with many different compilers (even for Symbian, ARM, Windows CE/Mobile...).
In my last and current company I work in mixed Linux- and Windows-developer teams, who work together using Subversion and Qt (which has a very easy and powerful build-system "QMake", which hides all the different platform/compiler specific build environments - you just have to write one build file for all platforms - so easy!).
And: Qt contains nearly everything you need:
simple string handling, with easy translation support and easy string conversion (utf8, utf16, asci...)
simple classes for file i/o, images, networking etc.
comfortable gui classes
graphical designer for the gui layout/design
graphical translation tool to create dynamic translations for your apps (you can run one binary with different selectable languages - even cyrillic, asian fonts...)
integrated testing framework (unit tests)
So Qt is a full featured and very reliable environment - I use it for 10 years.
And: Qt integrates seamlessly into IDEs like VC++, Eclipse or provides its own IDE "QtCreator".
see: http://www.trolltech.com + http://doc.trolltech.com
Best Regards,
Chris
I had such experience working in Acronis. We had the same codebase used to build binaries (fully packaged installers, actually) targetting Win32/64, Linux, and OS X (and a bunch of other more exotic platforms, such as EFI), with everyone working in their IDE of choice. The trick here is to avoid compiler- and IDE-specific solutions, and make your project fully buildable from clean cross-platform make files. Note that you can perfectly well use any make system together with VC++, so it isn't a problem (we used Watcom make for historical reasons, but I wouldn't recommend it).
One other trick you can do is add a make script that automatically produces project files from the input lists in your makefiles, for all IDEs you use (e.g. VS and Eclipse CDT). That way, every developer generates that stuff for himself, and then opens those projects in IDE to edit/build/debug, but the source repository only has makefiles in it.
Ensuring that code is compilable for everyone can be a problem, mostly because VC++ is generally more lax in applying the rules than g++ (for example, it will let you bind an rvalue to a non-const reference, albeit with a warning). If you compile with treat warnings as errors, and highest warning levels (with perhaps a few hand-picked warnings disabled), you will mostly avoid this. Having contiguous rolling build set up is another way to catch those early. One other approach we've used in Acronis is to have the Windows build environment have Cygwin-based cross-compilation tools in it, so any Windows dev could do a build targeting Linux (with the same g++ version and all) from his box, and see if that fails, to verify that his changes will compile in g++.
I personally use cmake/mingw32/Qt4 for all my C++ needs.
QtCreator is a crossplatform IDE which is somewhat optimized for qt4 programming.
We're working on a cross-platform project as well. We're using Emacs to code, SCons to build and Visual Studio 2008 to debug. It's my 1st time using Emacs + SCons and I must say that It's very very nifty once you figure out how does the SConstruct and SConscripts work.
I'm late to this question, and there are a lot of good answers here, but I haven't seen anyone enumerate all the issues I've run into (most of my work in C++ is and has been cross-platform), so:
Cross-platform compilation. Everyone has covered this quite well. CMake, and SCons are useful among others.
Platform differences. These aren't actually limited to Linux v Windows. Different versions of Windows have plenty of subtle issues. If you ever want to jump from Linux to OS X you'll find the same. So do processor architecture differences: 32 v 64 bit doesn't usually matter - until it does and things break on you very badly. This is something C++ programmers face more than in most other languages, whose implementations are working hard to hide this sort of thing from programmers.
Test everything. Alan mentions unit tests but let me emphasize them. The real problem with cross-platform programming is not code that won't compile: it's code that compiles just fine and does the wrong thing in subtle cases. Unit tests matter here much more than they do when you are working on a single platform.
Use portable libraries whenever possible. Getting this right is full of subtle gotchas. It's better to take advantage of someone else's work than to roll your own. Qt is useful here, as are NSPR and APR (the latter two are C, but wrappers exist if you don't want to mess with them directly.) These are the libraries that explicitly target platform abstraction as a goal, but most other libraries you use should be checked for this. Be reasonably wary of cool libraries that don't have a track record of portability. I'm not saying don't use them: just test first. Doing this right saves you enormous effort on testing.
Pavel mentions continual integration. This may not matter if there are only a handful of you. But I've found that the number of platforms you get when you consider all of the OS, OS variant, and processor differences means that without some form of continuous build-test cycle you will always be missing some edge case. If your project gets bigger than a handful of people consider doing this.
Version control. The most obvious issue is handling newlines and filename case-sensitivity but there are others. Most of the well-known open source VCSes do this right by now, but it's notable that it usually takes them several years to find all their bugs related to portability. As an example Mercurial, which has had portability as one of its selling points, has a series of fixes spanning three or four releases dealing with Windows filenames in uncommon situations. Make sure you can check out what the others check in early on.
Scripts. Use Perl/Python/Ruby (or something like them) for your scripting. Trying to keep Linux shell and Windows batch scripts in sync is painfully tedious.
Despite all of the above: overall cross-platform is quite doable, and there's no real reason to avoid it. My experience is that the problems tend to crop up sporadically, but when they do they take more than there fair share of time. If you've been programming a while though you won't find that unusual.
The issue is not really editing the C++ source files, but the build process itself. VS doesn't use the same Makefile architecture as Linux development typically does. A radical suggestion, but both groups could actually use the same C++ IDE and build process - see Code::Blocks for more info.
This is possible (I do that to earn my daily money :-) ).
But you have to keep in mind the differences in the OS supplied libraries which could be very annoying.
Two good options to get around that are BOOST and QT.
Both supply you with platform independent functions of useful stuff that isn't handled by the C lib and the STL.
I've done this in two ways:
Each platform has its own build system. Whenever someone changes something (adding a source file), they either adapt the other platform too, or they mail a notification and someone more familiar with the other platform adapts it accordingly.
Use CMake.
I can't say I really liked CMake, but a cross-platform tool certainly has its advantages.
Generally, using different compilers to compile your code from the very first few lines is always a good idea, as it helps improving code quality.
Another vote for cmake.
One thing to watch out for, filenames are cased but not case sensitive on Windows. They are case sensitive on most unix filesystems.
This is generally a problem with #include
eg. given a file FooBar.h
#include "foobar.h" // works on Windows, fails on Linux.
I do agree with everyone here that has suggested cmake for cross platform development in C++. It is an excellent tool.
Another thing I would suggest is to use eclipse CDT as development environment. It works in any place where you can run Java an gcc and that unifies your development environment.
I think that was Alan Jackson who gave emphasis to the unit test. For doing so yo would need some cross platform unit test libraries. I read this post time ago regarding C++ unit test frameworks. It is a bit outdated but thoughtful. The one missing that also works in both platforms is googletest.
Finally if you want to run those test automatically in both platforms cmake has another tool called ctest that is very good for doing so.
Check out premake... It is pretty similar to CMake but written using lua.
I have used this on a number of development projects and find it easy to learn and integrate into existing company and project structures.
Give it a try!
http://industriousone.com/premake