Step-by-step setup of a decent development environment under Windows

The compiler toolchain

  1. Fetch the latest MinGW toolchain setup (currently mingw-get-inst-20111118.exe).
  2. Run the setup, answering as required:
    • Download latest repository catalogues
    • Installation folder: C:\MinGW
    • Select installation of:
      • C Compiler
      • C++ Compiler
      • MSYS Basic System
      • MinGW Developer Toolkit
  3. Create the development root directory: D:\dev_root and the related shell launcher: D:\dev_root\dev_msys.bat which prepares a tailored environment before starting the MSYS shell:
    @echo off
    set drive=%~dp0
    set drivep=%drive%
    if $#\#$==$#%drive:~-1%#$ set drivep=%drive:~0,-1%
    set drive=

    set MSYS_ROOT=C:\MinGW\msys\1.0

    set DEV_ROOT=%drivep%
    set PATH=%DEV_ROOT%\dist\bin;%DEV_ROOT%\dist\lib;%PATH%

    set HOME=%DEV_ROOT%\dev
    if not exist %HOME% mkdir %HOME%

    set d=%MSYS_ROOT%\etc\profile.d
    if not exist %d% mkdir %d%
    if not exist %d%\home_profile.sh echo ^
    if [ -f $HOME/.profile ]; then . $HOME/.profile; fi > %d%\home_profile.sh
    set d=

    %MSYS_ROOT%\msys.bat
Optionally you may go further, providing your own .inputrc and .profile in the $HOME directory but you should keep in mind that these customizations would be restricted to the MSYS environment (and not be available anymore in the regular Win32 world).

Some extra libraries

OpenCL (AMD SDK)

Nothing is needed to be built. Just fetch the SDK from http://developer.amd.com/sdks/AMDAPPSDK/downloads/Pages/default.aspx (here at this time), start the setup and let the component install using default options.

Using MSYS from our shell launcher will let you verify that the SDK environment variables have been defined as required (AMDAPPSDKROOT, AMDAPPSDKSAMPLESROOT, PATH).

By the way, the following .profile excerpt would help simplify the "slash"/"anti-slash" mess between Win32/MSYS environment handling:

function slashify_path { echo $1 | sed -e "s,\\\\,/,g;s,\\([^:]\\)/$,\\1,;s,^\\(\\(.\\):\\),/\\2,"; }

export DEV_ROOT=`slashify_path $SOCV_ROOT`
export AMDAPPSDKROOT=`slashify_path "$AMDAPPSDKROOT"`
export OSTYPE HOSTTYPE

Environment preparation

Prepare environment for libraries needing local build:

  1. Open MSYS using with our shell launcher
  2. Prepare extra base directory
    $ mkdir $DEV_ROOT/dist
    $ mkdir $DEV_ROOT/tmp
    $ cd $DEV_ROOT/tmp

boost

  1. Navigate to http://www.boost.org and get v1.48.0 archive (latest at this time -- here).
  2. Extract archive...
    $ bzip2 -cd /d/.../boost_1_48_0.tar.bz2 | tar xvf -
  3. ...configure...
    $ cd $DEV_ROOT/tmp/boost_1_48_0

    $ ./bootstrap.sh --with-toolset=mingw
    Building Boost.Build engine with toolset mingw... tools/build/v2/engine/bin.ntx86/b2
    Unicode/ICU support for Boost.Regex?... not found.
    Backing up existing Boost.Build configuration in project-config.jam.2
    Generating Boost.Build configuration in project-config.jam...

    Bootstrapping is done. To build, run:

        ./b2

    To adjust configuration, edit 'project-config.jam'.
    Further information:

       - Command line help:
         ./b2 --help

       - Getting started guide:
         http://www.boost.org/more/getting_started/unix-variants.html

       - Boost.Build documentation:
         http://www.boost.org/boost-build2/doc/html/index.html

    $ mv project-config.jam project-config.jam_
  4. ...and build (patience required, see note)
    $ ./bjam --prefix=$DEV_ROOT/dist --layout=tagged --build-type=complete toolset=gcc install
    Performing configuration checks

        - has_icu builds           : no
    warning: Graph library does not contain MPI-based parallel components.
    note: to enable them, add "using mpi ;" to your user-config.jam
        - iconv (libc)             : no
        - iconv (separate)         : yes
        - icu                      : no
        - icu (lib64)              : no
        - g++ -shared-* supported  : no
        - ../config//has_gcc_visibility builds : yes
        - ../config//has_long_double_support builds : yes
    warning: skipping optional Message Passing Interface (MPI) library.
    note: to enable MPI support, add "using mpi ;" to user-config.jam.
    note: to suppress this message, pass "--without-mpi" to bjam.
    note: otherwise, you can safely ignore this message.
    warning: No python installation configured and autoconfiguration
    note: failed.  See http://www.boost.org/libs/python/doc/building.html
    note: for configuration instructions or pass --without-python to
    note: suppress this message and silently skip all Boost.Python targets

    Component configuration:

        - chrono                   : building
        - date_time                : building
        - exception                : building
        - filesystem               : building
        - graph                    : building
        - graph_parallel           : building
        - iostreams                : building
        - locale                   : building
        - math                     : building
        - mpi                      : building
        - program_options          : building
        - python                   : building
        - random                   : building
        - regex                    : building
        - serialization            : building
        - signals                  : building
        - system                   : building
        - test                     : building
        - thread                   : building
        - timer                    : building
        - wave                     : building

    ...patience...
    ...patience...
    ...patience...
    ...patience...
    ...found 26231 targets...

    $

Note: The build would be reeeaaaalllyy long on average platform.

Hopefully, it could be split into several runs, or with a restricted set of target according to your architecture needs.

You may safely substitute the --build-type=complete option with a combination of link=static,shared variant=debug,release threading=single,multi parameters as required.

For the record, all files needing to be built are compiled in $DEV_ROOT/tmp/boost_1_48_0/bin.v2 directory tree before being copied to installation location.

fltk

  1. Navigate to http://www.fltk.org and get v1.3.x archive (latest at this time -- here).
  2. Extract archive...
    $ bzip2 -cd /d/.../fltk-1.3.x-r9217.tar.bz2 | tar xvf -
  3. ...and build
    $ cd $DEV_ROOT/tmp/fltk-1.3.x-r9217
    $ ./configure --prefix=$DEV_ROOT/dist --enabled-shared
    ...
    config.status: creating FL/Makefile
    config.status: creating config.h

    $ make
    ...
    $ make install
    ...
    $

The development environment itself

Layout

The ideas behind the development tree layout are rather classic:

$HOME/project/
$HOME/project/Makefile
$HOME/project/bin/
...
$HOME/project/include/
...
$HOME/project/src/
$HOME/project/src/Makefile
$HOME/project/src/main.cpp
...

Makefiles

The project global makefile ($HOME/project/Makefile) is solely an automation helper, usually reduced to redirect target building to the source related makefile:

all:
        $(MAKE) -C src $@

.DEFAULT:
        $(MAKE) -C src $@

The source Makefile is providing the required informations needed to produce the specified target:

# Makefile --- Source Makefile
# ---------------------------------------------------------------------------
# This file defines module building features using default make rules.
#
# Standard variables usage:
#
# CC/CXX    Which compiler to use.
#
# CFLAGS    C Compiler options.
# CXXFLAGS  C++ Compiler options.
# CPPFLAGS  Preprocessor options.
# LDFLAGS   Linker options.
# LOADLIBES Linker search paths.
# LDLIBS    Linker libraries to use.
# ARFLAGS   Archiver options (static library modules).
#
# Module variable usage:
#
# NAME Module name.
# TYPE Module type to build ("staticlib", "sharedlib" or anything else to
#      make a regular executable).
#
# See 'stdrules.mak' for default targets
# ---------------------------------------------------------------------------

# c++ project
override CC = $(CXX)

ifeq ($(OS),Windows_NT)
  CXXFLAGS  = -fmessage-length=0 -Wall -fpermissive
  CPPFLAGS  = -I../include -I$(DEV_ROOT)/dist/include -I'$(AMDAPPSDKROOT)/include'
  LOADLIBES = -L$(DEV_ROOT)/dist/lib
  LDLIBS    =
else
  CXXFLAGS = -fmessage-length=0 -Wall -fPIC -fpermissive
  CPPFLAGS = -I../include -I'$(AMDAPPSDKROOT)/include'
endif
DEBUGFLAGS = -O0 -g3 -ggdb

NAME = my_project_binary
TYPE = binary

BASETARGETDIR = ../bin

# ---------------------------------------------------------------------------
# Retrieve standard building rules (do NOT delete following line)
include $HOME/common/stdrules.mak

Makefile helper

All the building job is made by the $HOME/common/stdrules.mak makefile helper shared between all project:

# stdrules.mak --- Standard Building Rules Makefile part
# ---------------------------------------------------------------------------
# This Makefile system relies on some features solely made available in the
# GNU environment.
#
# Required configuration variables:
#   NAME = <target base name>
#   TYPE = staticlib / sharedlib / executable
#   BASETARGETDIR = ../bin
# Targets defined:
#   'all'         Builds target.
#   'mostlyclean' Removes objects and source backups.
#   'clean'       Removes objects and target.
#   'clobber'     cf. clean also removing generated dependency files.
# ---------------------------------------------------------------------------
# Useful references:
# http://www.gnu.org/software/make/manual/make.html#Prerequisite-Types
# http://www.gnu.org/software/make/manual/make.html#Automatic-Prerequisites
# http://www.mingw.org/wiki/sampleDLL
#
#

ifeq ($(OS),Windows_NT)
  ECHO = /bin/echo.exe -e
  MKDIR = /bin/mkdir.exe -p
  CP = /bin/cp.exe
  SED = /bin/sed.exe
else
  ECHO = /bin/echo -e
  MKDIR = /bin/mkdir -p
  CP = /bin/cp
  SED = /bin/sed
endif

ifeq ($(OSTYPE),)
  OSTYPE = `uname -o`
  $(info OSTYPE set to $(OSTYPE))
endif
ifeq ($(HOSTTYPE),)
  HOSTTYPE = `uname -m`
  $(info HOSTTYPE set to $(HOSTTYPE))
endif
ARCH = $(OSTYPE)-$(HOSTTYPE)

ifeq ($(BASETARGETDIR),)
  BASETARGETDIR = ../bin
  $(info BASETARGETDIR set to $(BASETARGETDIR))
endif

ifeq ($(origin DEBUG), undefined)
  TARGETDIR = $(BASETARGETDIR)/$(ARCH)
else
  CFLAGS += $(DEBUGFLAGS)
  CXXFLAGS += $(DEBUGFLAGS)
  TARGETDIR = $(BASETARGETDIR)-debug/$(ARCH)
endif

ifneq (,$(findstring staticlib,$(TYPE)))
  TARGET = lib$(NAME).a
  TARGETRECIPE = $(AR) $(ARFLAGS) $(TARGETDIR)/$(TARGET) $?
#  $(info ($(TYPE)) TARGETRECIPE = $(TARGETRECIPE))
else
  ifneq (,$(findstring sharedlib,$(TYPE)))
    ifeq ($(OS),Windows_NT)
      TARGET = $(NAME).dll
      TARGETIMP = $(TARGETDIR)/lib$(NAME).dll.a
      TARGETRECIPE = $(LINK.o) -shared $^ -Wl,--out-implib,$(TARGETIMP) $(LOADLIBES) $(LDLIBS) -o $@
    else
      TARGET = lib$(NAME).so
      TARGETRECIPE = $(LINK.o) -shared $^ $(LOADLIBES) $(LDLIBS) -o $@
    endif
#    $(info ($(TYPE)) TARGETRECIPE = $(TARGETRECIPE))
  else
    TARGET = $(NAME)
    TARGETRECIPE = $(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@
#    $(info ($(TYPE)) TARGETRECIPE = $(TARGETRECIPE))
  endif
endif

#SOURCES = $(strip $(wildcard *.c) \
#                  $(wildcard *.cc) \
#                  $(wildcard *.cpp) \
#                  $(wildcard *.cxx) \
#           )
OBJECTS = $(strip $(patsubst %.c,$(TARGETDIR)/%.o,$(wildcard *.c)) \
                  $(patsubst %.cc,$(TARGETDIR)/%.o,$(wildcard *.cc)) \
                  $(patsubst %.cpp,$(TARGETDIR)/%.o,$(wildcard *.cpp)) \
                  $(patsubst %.cxx,$(TARGETDIR)/%.o,$(wildcard *.cxx)) \
            )

DEPENDENCIES = $(OBJECTS:%.o=%.d)
#$(info DEPENDENCIES = $(DEPENDENCIES))

.PHONY: all mostlyclean clean clobber run debug
all: $(TARGETDIR)/$(TARGET)

$(TARGETDIR)/$(TARGET): $(OBJECTS)
        $(TARGETRECIPE)

mostlyclean:
        $(RM) $(OBJECTS) *~

clean:
        $(RM) $(TARGETDIR)/$(TARGET) $(TARGETIMP) $(OBJECTS)

clobber:
        $(RM) $(TARGETDIR)/$(TARGET) $(TARGETIMP) $(OBJECTS) $(DEPENDENCIES)

run: $(TARGETDIR)/$(TARGET)
        $(TARGETDIR)/$(TARGET)

### target dir existence rules

$(TARGETDIR)/$(TARGET) $(TARGETIMP) $(OBJECTS) $(DEPENDENCIES): | $(TARGETDIR)

$(TARGETDIR):
        $(MKDIR) $(TARGETDIR)

### dependencies building rules in target dir

# DEPENDENCYRECIPE.c = \
#   set -e; $(RM) $@; \
#   $(ECHO) '$$(info including $@...)' > $@.$$$$; \
#   $(CC) -MM $(CPPFLAGS) -MP $< >> $@.$$$$; \
#   $(SED) 's,\($*\)\.o[ :]*,$(TARGETDIR)/\1.o $@ : ,g' < $@.$$$$ > $@; \
#   $(RM) $@.$$$$

# DEPENDENCYRECIPE.cc = \
#   set -e; $(RM) $@; \
#   $(ECHO) '$$(info including $@...)' > $@.$$$$; \
#   $(CXX) -MM $(CPPFLAGS) -MP $< >> $@.$$$$; \
#   $(SED) 's,\($*\)\.o[ :]*,$(TARGETDIR)/\1.o $@ : ,g' < $@.$$$$ > $@; \
#   $(RM) $@.$$$$

DEPENDENCYRECIPE.c = \
  $(ECHO) '$$(info including $@...)' > $@; \
  $(CC) $(CPPFLAGS) -MM -MP -MT '$(patsubst %.d,%.o,$@) $@' $< >> $@

DEPENDENCYRECIPE.cc = \
  $(ECHO) '$$(info including $@...)' > $@; \
  $(CXX) $(CPPFLAGS) -MM -MP -MT '$(patsubst %.d,%.o,$@) $@' $< >> $@
#$(info DEPENDENCYRECIPE.cc = $(DEPENDENCYRECIPE.cc))

.PRECIOUS: $(TARGETDIR)/%.d

$(TARGETDIR)/%.d: %.c
        @$(ECHO) 'generating $@...'
        @$(DEPENDENCYRECIPE.c)

$(TARGETDIR)/%.d: %.cc
        @$(ECHO) 'generating $@...'
        @$(DEPENDENCYRECIPE.cc)

$(TARGETDIR)/%.d: %.cpp
        @$(ECHO) 'generating $@...'
        @$(DEPENDENCYRECIPE.cc)

$(TARGETDIR)/%.d: %.cxx
        @$(ECHO) 'generating $@...'
        @$(DEPENDENCYRECIPE.cc)

### objects building rules in target dir

$(TARGETDIR)/%.o : %.cc $(TARGETDIR)/%.d
        $(COMPILE.cc) $(OUTPUT_OPTION) $<

$(TARGETDIR)/%.o : %.cpp $(TARGETDIR)/%.d
        $(COMPILE.cpp) $(OUTPUT_OPTION) $<

$(TARGETDIR)/%.o : %.cxx $(TARGETDIR)/%.d
        $(COMPILE.cxx) $(OUTPUT_OPTION) $<

### using generated dependencies
-include $(DEPENDENCIES)