Step-by-step setup of a decent development environment under Windows
The compiler toolchain
- Fetch the latest MinGW toolchain setup (currently
mingw-get-inst-20111118.exe
).
- 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
- 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:
- Open MSYS using with our shell launcher
- Prepare extra base directory
$ mkdir $DEV_ROOT/dist
$ mkdir $DEV_ROOT/tmp
$ cd $DEV_ROOT/tmp
boost
- Navigate to http://www.boost.org and get v1.48.0 archive (latest at this time -- here).
- Extract archive...
$ bzip2 -cd /d/.../boost_1_48_0.tar.bz2 | tar xvf -
- ...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_
- ...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
- Navigate to http://www.fltk.org and get v1.3.x archive (latest at this time -- here).
- Extract archive...
$ bzip2 -cd /d/.../fltk-1.3.x-r9217.tar.bz2 | tar xvf -
- ...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:
- A directory tree per project producing a single binary target.
- Binaries and intermediate files are build in a platform/configuration dependent sub-directory.
$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)