I will relate my experience with deploying an Ogre-powered application on the three major platforms. You will find some tips and things to watch out for when you're deploying your own, I hope it helps!
Table of contents
- Deploying on Windows
- MSVC++ Runtime Libraries
- DirectX End-User Runtime Libraries
- The UAC
- Deploying on Mac OS X
- Deploying on Linux
- General Stuff
- Generic Makefile
Windows is likely the easiest platform to deploy for and the process is more or less smooth. There are mainly three things you have to watch out for:
You will either have to depend on your users having these runtime libraries (which you should never do) or supply them with your package. It is relatively harmless and works most of the time to do the latter, you have to put the following 2 DLL files in the same folder as your executable:
The EULA for those libraries permits you to freely redistribute them without having to show the EULA somewhere in your package (or installer). (Please verify this)
This one is a little more tricky. There are a few methods to detect the DirectX version installed on the user's system in-code, but it's not always valid. Starting from Windows XP SP2, all users have DirectX 9.0c installed, but for some reason, do not have the libraries Ogre requires. The solution I found best was to install the required libraries using the DirectX redistributable package within my installer. There are a few ways to go on about doing this:
Technically, this method ensures that the user will have all the required libraries installed before running your game, but it has the disadvantage that you can not control which libraries are downloaded, and at the time of writing, the package weighs at about 97 Mbytes. For me that was unacceptable, as the whole game weighed < 50 Mbytes.
Caveats: the installation will not succeed without admin privileges, requires an internet connection, and is a big download
2. Supplying a trimmed-down version of the DX redistributable to be run manually by the user, or as a post-installation step of your installer.
What I didn't like about this method is that the user can cancel the installation, which defeats the whole point.
Caveats: the installation will not succeed without admin privileges, and/or there's no guarantee it will ever be allowed to run( in case the user cancels it )
Personally, I found this to be the best method; trim down the redistributable DX package to what you need like in solution #2, however, the user will not even see that you're running the installer and thus they cannot skip it. In the case the user has no admin access or already has the libraries installed, it will also silently fallback and do nothing.
This is achieved by running the installer with the /silent flag: dxsetup.exe /silent
Caveats: Also requires admin privileges, but falls back gracefully
NOTE: You MUST include and have the user accept the DirectX EULA somewhere in your installation. You may combine it with other license agreements.
Please refer to this Microsoft article for more info regarding how to trim down the DirectX redistributable package.
If you're automatically setting a RenderSystem using the config file (ogre.cfg) then you should know that if the specified RS can not be loaded / started, your game will abort. So for that reason, it's good to attempt to load the RS manually in your code; what I do for Windows is that I try to load the Direct3D_Renderer plugin first, and if that throws an exception, I resort to RenderSystem_GL. This way, you can be sure that your game will run no matter what. However, an error message complaining about a missing DLL will still be shown, and I haven't found out how to get around that. Please edit this information in, if you should come across it...
There is a wiki entry that explains how to load RenderSystems manually.
Starting from Windows Vista, applications installed in a system directory (such as ProgramFiles) require admin privileges to run. The symptoms of this issue could be very frustrating because your game will simply die once a non-admin user tries to run it, and the crash log isn't anything useful. Attempting to run the application as an administrator will solve the problem (you can test by right-clicking then "Run as Administrator").
The only way to fix this is to make a per-user installation which will install your game in the user's personal area/directory. The problem with this approach is that you will end up with multiple copies of the game distributed across user folders and maintaining patches will be very problematic: when user A runs the patcher for your game and downloads it, user B will still have to do the same thing because user A can't patch the game in user B and so on.
You have to decide on this: Either make the installation require admin access, then it's shared across all the users (not really, they still have to have privileges to run it, but maybe it can be scripted to give all users access? If someone knows about this, please revise this section) or per-user. This can usually be specified by the installer you're creating.
You can create .MSI installers that facilitate the process of copying your game files and several other things such as adding registry entries, creating shortcuts, and even chaining other .MSI installations. There are many tools for this, free and not. One is directly available from Microsoft's Visual Studio suite, but you are not limited to that, you can just use google to find other MSI creators.
The process is straightforward until you start resolving the dependencies of your used Frameworks / dylibs. I must note that you might not have to do at all, because it depends on how the libraries you use are built. I did not have to do this with the Ogre framework or plugins, but I did with my custom built libraries and external ones (like BulletPhysics frameworks for example).
Apparently, when I built the Bullet frameworks using XCode, they linked against a hard-coded system path, and so when I tried running the application on another machine, the linked-against frameworks could not be found, even though they are contained in my bundle. So, in a nutshell, what we need to do is change the paths of our bundled dependencies.
Okay, let's start off by finding out our dependencies. We can do this using the command otool -L file (this is similar to Linux's ldd or objdump.)
So, let's say our executable depends on a framework called BulletCollision.framework and from the result of the earlier command we see it points to /Library/Frameworks/BulletCollision.framework.
Now we know that there's a very little chance our users will have that framework installed, so we supply it in our bundle under Game.app/Contents/Frameworks/BulletCollision.framework (this is done in XCode's copy frameworks phase.) If we run otool -L BulletCollision.framework/BulletCollision we find that the framework itself has a hard-link against LinearMath.framework, another framework, so we will have to also relink the framework and not just our executable.
Now what we are going to do is to tell our executable to look at the bundled framework instead of the one installed in our /Library.
It's a 2-stage process:
- relink each of the framework's dependencies to point to our bundled versions
- relink our executable to point to our bundled dependencies versions
We will be performing this re-linking using the command install_name_tool. There is a special argument called @executable_path that points to the executable being run within a bundle (by default Game.app/Contents/MacOS). We will be using that to point dynamically to our dependencies.
(You can either wrap this up in a shell script to be run a post-install script or do it manually. Optimally, you have to do it once – for all frameworks / dependencies, but it's tedious work)
# our current directory is: Game.app/Contents # @executable_path will point at: Game.app/Contents/MacOS # first, we change the identifier of the Framework install_name_tool -id @executable_path/../Frameworks/BulletCollision.framework/Versions/2.77/BulletCollision Frameworks/BulletCollision.framework/Versions/2.77/BulletCollision # BulletCollision.framework depends on LinearMath.framework which resides in /Library/Frameworks # but we also are bundling it, so we have to modify BulletCollision.framework to point to our # bundled version of LinearMath.framework install_name_tool -change /Library/Frameworks/LinearMath.framework/Versions/2.77/LinearMath @executable_path/../Frameworks/LinearMath.framework/Versions/2.77/LinearMath Frameworks/BulletCollision.framework/BulletCollision # finally, relink our executable to point at the newly identified bundled Framework install_name_tool -change /Library/Frameworks/BulletCollision.framework/Versions/2.77/BulletCollision @executable_path/../Frameworks/BulletCollision.framework/Versions/2.77/BulletCollision MacOS/MyGame
Test that your newly linked frameworks and executable are correctly pointing to the bundled versions by running otool -L executable on each of them.
Note: I am pretty sure that this is a problem you should not be dealing with in the first place; the libraries themselves are probably built incorrectly, since Ogre's framework and plugins do NOT require this hassle. But it's good to know how to deal with them if you have to, as this has costed me a few hours to figure out.
There are special compiler flags you need to pass to GCC/G++ to compile, namely:
CXXFLAGS= -dynamiclib -fPIC -arch i386 -install_name @executable_path/../Frameworks/YourDylib.dylib
Tune -arch to your requirements.
Mac OS X applications are usually distributed in .DMG files which basically compress your application bundle and provide the user with an interface to move your bundle to their Applications folder. There are several tools available to help you do this, or you can do it through the Terminal. I personally use DropDMG.
Personally, I found this to be a nightmare. There are apparently 3+ methods you can follow, none of which is straightforward and the caveats are not clear. I will outline them briefly and explain what I used:
One choice is to statically build all the libraries your executable depends on and then packaging the binary and the game resources without worrying about the libraries. Static libraries on linux end with .a extension, unlike dynamic/shared libraries which end in .so. Here (StackOverflow) is a good article that explains the differences between the two build methods if you're not familiar with them.
For us Ogre users, we don't really need to worry about dylibs saving resources.. there will rarely be more than 1 process using Ogre / your game libraries at the same time unless you're planning to support “multiboxing”. What might matter is the ability to update the libraries your game/application uses, and it's harder to do so with static builds.
To build Ogre statically, you only need to change a flag in Ogre's Cmake project. Please make sure you read the wiki entry for building Ogre. You must also statically build every other library you're using, and make sure you build your executable against those statically linked ones (-lOgreMainStatic for example).
To check your executable / library dependencies and verify that you've correctly built it statically, you can use the command ldd my_executable or objdump -x my_executable | grep NEEDED .. there should be NO dependencies except for the low-level/system ones!
The way to go on about doing this is to supply all the libraries your application depends on with the rest of your resources and binary, and then manipulating the LD_LIBRARY_PATH environment variable to tell it where your libraries are. To do this in a nice, non-obtrusive way we can wrap our game with a shell script that modifies the argument only for the duration of your application's lifetime.
How do I know which libraries I need to supply?
Using ldd and objdump as outlined above you can find all the dependencies. The list might be a bit long, but you can safely assume that most low-level libraries are provided, particularly OpenGL/X11/gcc ones.
How do I run the application?
Assuming that your binary resides in app_root/bin folder, and your libraries (.so) files are in app_root/lib, here is an example script of how to do this (let's call it startup_script.sh):
#!/usr/bin/env bash # if this is a symlink to the game's binary, we need to resolve it and change directory if [ -h $0 ]; then DIR="$( cd "$( dirname `readlink $0` )" && pwd )" cd $DIR fi # point to our libraries and run the game LD_LIBRARY_PATH=../lib:$LD_LIBRARY_PATH ./my_executable
Then running the application is as simple as calling the script: ./startup_script.sh
It's nice for your script to be symlink-aware so that the users may soft-link it anywhere and run it from anywhere (and create launchers). This is done by ln -s /path/to/your/game/bin/startup_script.sh /usr/local/bin/your_game
Note: make sure the libraries are named exactly as they are linked against / required in your executable, including the extension/version number. Verify that by running ldd your_executable. Also make sure the script is executable by running chmod +x startup_script.sh
Sadly, I could not get this to work because at the time of writing, the official Ubuntu repository has an old version of Ogre (1.6.5) and my game required 1.7.2 so there was no way to do this. However, this could be possible if you plan on using non-official repositories (or even rolling your own.) Ogre already hosts a repository with the latest versions.
Do note that to create a .deb package, you must provide the source code as it will be built and all the required dependencies will also be fetched from the repository and built if needed.
Depends on whether your project is open-sourced or not, and on whether your targeted userbase will be having the dependencies built or not. CMake is nice, try it out!
As a last note, there is no need to exclusively use any one of these methods; I don't see any reason why you can't mix both methods 1 & 2 using both dynamic and static libraries. Just make sure to consult the licenses of the libraries you're linking and that they conform with how you're linking them.
If you don't want to show Ogre's config dialog, it's nice to set some default video settings for the first time your users run your application (granted that you're using a config file to parse/store video settings). This is a sample ogre.cfg I used for deploying on Windows:
[Direct3D9 Rendering Subsystem] Allow NVPerfHUD=No FSAA=0 Floating-point mode=Fastest Full Screen=Yes Resource Creation Policy=Create on all devices VSync=No VSync Interval=1 Video Mode=1024 x 768 @ 32-bit colour sRGB Gamma Conversion=No [OpenGL Rendering Subsystem] Colour Depth=32 Display Frequency=60 FSAA=0 Full Screen=Yes RTT Preferred Mode=FBO VSync=No VSync Interval=1 Video Mode=1024 x 768 sRGB Gamma Conversion=No
Note: We do NOT specify a RenderSystem in the config file! That would force a default renderer and as we outlined above in the Windows section that is not a recommended thing to do. This way, whichever RenderSystem is loaded by your code, it will be run by these settings. Also, be sure to remove the “Rendering Device=” setting in the D3D section as that's specific to the hardware!
Now we can know that all users will run with at least 1024 x 768 resolution, and of course you should tune that to your requirements.
If you are not comfortable or have a reason not to use CMake or other auto-build tools, here is a generic Makefile that you can easily customize to fit a wide range of projects:
############################################################################# # # Generic Makefile for C/C++ Program # # License: GPL (General Public License) # Author: whyglinux <whyglinux AT gmail DOT com> # Date: 2006/03/04 (version 0.1) # 2007/03/24 (version 0.2) # 2007/04/09 (version 0.3) # 2007/06/26 (version 0.4) # 2008/04/05 (version 0.5) # # Description: # ------------ # This is an easily customizable makefile template. The purpose is to # provide an instant building environment for C/C++ programs. # # It searches all the C/C++ source files in the specified directories, # makes dependencies, compiles and links to form an executable. # # Besides its default ability to build C/C++ programs which use only # standard C/C++ libraries, you can customize the Makefile to build # those using other libraries. Once done, without any changes you can # then build programs using the same or less libraries, even if source # files are renamed, added or removed. Therefore, it is particularly # convenient to use it to build codes for experimental or study use. # # GNU make is expected to use the Makefile. Other versions of makes # may or may not work. # # Usage: # ------ # 1. Copy the Makefile to your program directory. # 2. Customize in the "Customizable Section" only if necessary: # * to use non-standard C/C++ libraries, set pre-processor or compiler # options to <MY_CFLAGS> and linker ones to <MY_LIBS> # (See Makefile.gtk+-2.0 for an example) # * to search sources in more directories, set to <SRCDIRS> # * to specify your favorite program name, set to <PROGRAM> # 3. Type make to start building your program. # # Make Target: # ------------ # The Makefile provides the following targets to make: # $ make compile and link # $ make NODEP=yes compile and link without generating dependencies # $ make objs compile only (no linking) # $ make tags create tags for Emacs editor # $ make ctags create ctags for VI editor # $ make clean clean objects and the executable file # $ make distclean clean objects, the executable and dependencies # $ make help get the usage of the makefile # #=========================================================================== ## Customizable Section: adapt those variables to suit your program. ##========================================================================== # The pre-processor and compiler options. MY_CFLAGS = `pkg-config --cflags OGRE OIS` \ -Iinclude # --CHAIN ANY OTHER HEADER DIRECTORIES YOU NEED HERE-- # The linker options. MY_LIBS = `pkg-config --libs OGRE OIS` \ -lMyCustomLibrary # --CHAIN ANY OTHER LIBRARIES YOU NEED TO LINK AGAINST HERE-- # The pre-processor options used by the cpp (man cpp for more). CPPFLAGS = # The options used in linking as well as in any direct use of ld. LDFLAGS = # The directories in which source files reside. # If not specified, only the current directory will be serached. SRCDIRS = src # The executable file name. # If not specified, current directory name or `a.out' will be used. PROGRAM = bin/MY_APPLICATION ## Implicit Section: change the following only when necessary. ##========================================================================== # The source file types (headers excluded). # .c indicates C source files, and others C++ ones. SRCEXTS = .c .C .cc .cpp .CPP .c++ .cxx .cp # The header file types. HDREXTS = .h .H .hh .hpp .HPP .h++ .hxx .hp # The pre-processor and compiler options. # Users can override those variables from the command line. CFLAGS = -O2 CXXFLAGS= -O2 # The C program compiler. #CC = gcc # The C++ program compiler. #CXX = g++ # Un-comment the following line to compile C programs as C++ ones. #CC = $(CXX) # The command used to delete file. #RM = rm -f ETAGS = etags ETAGSFLAGS = CTAGS = ctags CTAGSFLAGS = ## Stable Section: usually no need to be changed. But you can add more. ##========================================================================== SHELL = /bin/sh EMPTY = SPACE = $(EMPTY) $(EMPTY) ifeq ($(PROGRAM),) CUR_PATH_NAMES = $(subst /,$(SPACE),$(subst $(SPACE),_,$(CURDIR))) PROGRAM = $(word $(words $(CUR_PATH_NAMES)),$(CUR_PATH_NAMES)) ifeq ($(PROGRAM),) PROGRAM = a.out endif endif ifeq ($(SRCDIRS),) SRCDIRS = . endif SOURCES = $(foreach d,$(SRCDIRS),$(wildcard $(addprefix $(d)/*,$(SRCEXTS)))) HEADERS = $(foreach d,$(SRCDIRS),$(wildcard $(addprefix $(d)/*,$(HDREXTS)))) SRC_CXX = $(filter-out %.c,$(SOURCES)) OBJS = $(addsuffix .o, $(basename $(SOURCES))) DEPS = $(OBJS:.o=.d) ## Define some useful variables. DEP_OPT = $(shell if `$(CC) --version | grep "GCC" >/dev/null`; then \ echo "-MM -MP"; else echo "-M"; fi ) DEPEND = $(CC) $(DEP_OPT) $(MY_CFLAGS) $(CFLAGS) $(CPPFLAGS) DEPEND.d = $(subst -g ,,$(DEPEND)) COMPILE.c = $(CC) $(MY_CFLAGS) $(CFLAGS) $(CPPFLAGS) -c COMPILE.cxx = $(CXX) $(MY_CFLAGS) $(CXXFLAGS) $(CPPFLAGS) -c LINK.c = $(CC) $(MY_CFLAGS) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) LINK.cxx = $(CXX) $(MY_CFLAGS) $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) .PHONY: all objs tags ctags clean distclean help show # Delete the default suffixes .SUFFIXES: all: $(PROGRAM) # Rules for creating dependency files (.d). #------------------------------------------ %.d:%.c @echo -n $(dir $<) > $@ @$(DEPEND.d) $< >> $@ %.d:%.C @echo -n $(dir $<) > $@ @$(DEPEND.d) $< >> $@ %.d:%.cc @echo -n $(dir $<) > $@ @$(DEPEND.d) $< >> $@ %.d:%.cpp @echo -n $(dir $<) > $@ @$(DEPEND.d) $< >> $@ %.d:%.CPP @echo -n $(dir $<) > $@ @$(DEPEND.d) $< >> $@ %.d:%.c++ @echo -n $(dir $<) > $@ @$(DEPEND.d) $< >> $@ %.d:%.cp @echo -n $(dir $<) > $@ @$(DEPEND.d) $< >> $@ %.d:%.cxx @echo -n $(dir $<) > $@ @$(DEPEND.d) $< >> $@ # Rules for generating object files (.o). #---------------------------------------- objs:$(OBJS) %.o:%.c $(COMPILE.c) $< -o $@ %.o:%.C $(COMPILE.cxx) $< -o $@ %.o:%.cc $(COMPILE.cxx) $< -o $@ %.o:%.cpp $(COMPILE.cxx) $< -o $@ %.o:%.CPP $(COMPILE.cxx) $< -o $@ %.o:%.c++ $(COMPILE.cxx) $< -o $@ %.o:%.cp $(COMPILE.cxx) $< -o $@ %.o:%.cxx $(COMPILE.cxx) $< -o $@ # Rules for generating the tags. #------------------------------------- tags: $(HEADERS) $(SOURCES) $(ETAGS) $(ETAGSFLAGS) $(HEADERS) $(SOURCES) ctags: $(HEADERS) $(SOURCES) $(CTAGS) $(CTAGSFLAGS) $(HEADERS) $(SOURCES) # Rules for generating the executable. #------------------------------------- $(PROGRAM):$(OBJS) ifeq ($(SRC_CXX),) # C program $(LINK.c) $(OBJS) $(MY_LIBS) -o $@ @echo Type ./$@ to execute the program. else # C++ program $(LINK.cxx) $(OBJS) $(MY_LIBS) -o $@ @echo Type ./$@ to execute the program. endif ifndef NODEP ifneq ($(DEPS),) sinclude $(DEPS) endif endif clean: $(RM) $(OBJS) $(PROGRAM) $(PROGRAM).exe distclean: clean $(RM) $(DEPS) TAGS # Show help. help: @echo 'Generic Makefile for C/C++ Programs (gcmakefile) version 0.5' @echo 'Copyright (C) 2007, 2008 whyglinux <email@example.com>' @echo @echo 'Usage: make [TARGET]' @echo 'TARGETS:' @echo ' all (=make) compile and link.' @echo ' NODEP=yes make without generating dependencies.' @echo ' objs compile only (no linking).' @echo ' tags create tags for Emacs editor.' @echo ' ctags create ctags for VI editor.' @echo ' clean clean objects and the executable file.' @echo ' distclean clean objects, the executable and dependencies.' @echo ' show show variables (for debug use only).' @echo ' help print this message.' @echo @echo 'Report bugs to <whyglinux AT gmail DOT com>.' # Show variables (for debug use only.) show: @echo 'PROGRAM :' $(PROGRAM) @echo 'SRCDIRS :' $(SRCDIRS) @echo 'HEADERS :' $(HEADERS) @echo 'SOURCES :' $(SOURCES) @echo 'SRC_CXX :' $(SRC_CXX) @echo 'OBJS :' $(OBJS) @echo 'DEPS :' $(DEPS) @echo 'DEPEND :' $(DEPEND) @echo 'COMPILE.c :' $(COMPILE.c) @echo 'COMPILE.cxx :' $(COMPILE.cxx) @echo 'link.c :' $(LINK.c) @echo 'link.cxx :' $(LINK.cxx) ## End of the Makefile ## Suggestions are welcome ## All rights reserved ## #############################################################################
All you would normally have to tune is SRCDIRS, MY_CFLAGS and MY_LIBS.