Porting Echo to a new platform requires quite a bit of work. This document lists each of the things you need to address and explains each in detail.
=Setup=
==Organising your files==
Any source files you want to add should go into `src/Platforms/YourPlatform` and headers in `include/echo/Platforms/YourPlatform`
==NetbBeans project configuration==
Open the project properties window and click on the "Manage Configurations" button.We use environment variables defined in `env/YourPlatform-prebuild.config` to set compiler and linker flags. This is so a single point of change can persist for all projects if they are set up to reference these variables.
In the `env` folder the `*-prebuild.config` files define flags to be used by the various build tools. NetBeans Project configuration files usually reference these parameters.
The `*-postbuild.sh` scripts are scripts to execute after NetBeans has compiled and linked. These scripts are used as a work around to allow a post build step which is required to complete the build process for some platforms. Unfortunately this is a limitation of NetBeans, Either create a new configuration or duplicate another one if it is closer to what you already wanthopefully in the future this will be resolved.
To changeCopy the prebuild and postbuild files for the toolchain,platform that closely matches yours. configure toolchains in Netbeans preferencesYou will need to rename them to `YourPlatform-prebuild.config` and `YourPlatform-postbuild.sh`. Now you'll be able to select the toolchain in the properties of your configuration under "General"Open the prebuild configuration file and rename each occurrence of `ECHO_VAR_RELEASE_` to `ECHO_VAR_YOURPLATFORM_` keeping the naming convention consistent.
Add a platform preprocessor definition `ECHO_PLATFORM_YOURPLATFORM`Modify each variable as appropriate for your target. For example, you may need to modify compiler settings or library dependencies.
Now, in NetBeans open the echo3 project then open the project properties window. Click on the "Manage Configurations" button. Either create a new configuration or duplicate another one if it is closer to what you already want (often the case).
You'll need to modify the project properties so that the variables are used.
|Option|Location|Setting|
|---|---|---|
|Include Directories|Build -> C++Compiler|include;${ECHO_ENGINE_DEPENDENCIES_DIR}/opt/yourplatform/include|
|Preprocessor Definitions|Build -> C++Compiler|ECHO_PLATFORM_YOURPLATFORM|
|Ouput|Build->Linker|${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/libecho3.${CND_DLIB_EXT}|
|Additional Library Directories|Build->Linker|${ECHO_ENGINE_INSTALL_DIR}/dist/${CND_CONF}/${CND_PLATFORM};${ECHO_ENGINE_DEPENDENCIES_DIR}/opt/yourplatform/lib|
|Configuration Type|General|Set to static or dynamic library as necessary. Be sure to consider third party licencing requirements.|
|Runetime Search Directories|Build -> Linker|${ECHO_ENGINE_INSTALL_DIR}/dist/${CND_CONF}/${CND_PLATFORM};${ECHO_ENGINE_DEPENDENCIES_DIR}/opt/linux/lib|
NOTE: The paths for dependencies that include `yourplatform` need to be set to the path to match your dependency build location. See Building Dependencies for more information.
You will need to set any additional options as needed.
Finally, to tell NetBeans which toolchain to use for building you may need to set up a custom toolchain. Unfortunately these settings don't move around with the project since they are system independent. When we come up with a way to transfer these settings automatically we'll add instructions to do so. In the mean time you might need to supply any special steps required to setup the toolchain for your target.
To add the toolchain, configure toolchains in Netbeans preferences. Define each tool (e.g. which g++ to use). Now in the Project options in Build you should be able to select the new toolchain with the "Tool Collection" option.
==Building dependencies==
To documentdocument. In the mean time, see echo-dependencies for any instructions. Summary:
- `edepbuild` essentially wraps `./configure; make` and allows you to override some variables, or the entire build step, for each library with a config file. (You can tell it to use cmake instead as well).
- libraries to build are in the X folder (run the script to acquire if they aren't) and should not contain version numbers.
- Copy an existing `echo-dependencies/scripts` folder that closely matches your platform target.
- Rename each config file with the platform prefix you want to use for dependencies (this is the dependency target name and also the "install" folder location which you should use as the `yourplatform`.
- `yourplatform-library.config` needs to be updated for each library. Refer to other scripts to see various examples of how to deal with different libraries as some aren't maintained as well as others or the versions we are using require some manual steps along the way. It is not uncommon to see `sed` used in a script to replace some build script option (One rule is we don't want to modify the library source as it makes maintenance trickier when you want to update the library).
- Run ./edepbuild yourplatform library to just build the one library. If your build fails, it is recommended to delete the `build/yourplatform` folder to start from a clean environment for that library, for some libraries that use cmake, cmake will create temporary files that persist some settings which might cause a second build to succeed or a previously successful step fail. This means you might commit scripts that don't build from scratch.
- Make sure all required libraries build. If your platform doesn't need one of the dependencies because it is provided by the system you can skip it with `ONLY_SETUP=1` in your configuration file. Some platforms might also need a dependency that all others don't, in this case you will need to set up a script for each other platform (at least until we make some improvements to `edepbuild`.
=Code=
NOTE: When creating files for your platform you will need to add them to the project. When you do so, make sure you set all configurations to exclude your new file. For example, if you create `src/Platforms/YourPlatforms/SomeGraphicsImplementation.cpp` you will need to exclude it from all other configurations to prevent it from being built, similarly you'll need to exclude any files you don't want to build for your platform. To exclude files from a configuration, select one or more files in the project file list and select properties. There should be an Exclude from build check box you need to check.
`Platform` can mean two things in Echo and it depends on the context to what you refer to. Normally it means a physical device or software target such as a console or operating system. But when you are implementing your target platform "Platform" can also refer to a API implementation of a particular system. For example OpenAL is considered a target since it is portable. Rather than implementing an OpenAL implementation (or copying it) for your device target, it makes more sense to just use the existing OpenAL implementation. Normally this broader term applies when discussing the implementation details of your target.
A platform target can utilise existing system implementations simply by including or excluding the corresponding files from the build configuration. It is recommended to do so when possible to save effort and reduce maintenance overhead. On that note though, you can include multiple implementations in your build, just make sure the most appropriate one is used as the default.
==Types.h==
You may need to configure your platform types if boost cannot give you type sizes. If you can rely on boost to do type size detection then you can skip to the next section.
If that is the caseIf you need to define basic types then create `echo/Platforms/YourPlatform/Types.h` Define the following types in this file:
```
u8
u16
u32
u64
s8
s16
s32
s64
f32
f64
```
Define either `ECHO_LITTLE_ENDIAN` or `ECHO_BIG_ENDIAN` to suit your platform.
For example this is compatible with gcc:
```
#ifndef ECHO_ENDIAN_DETECTED
#if defined(__BYTE_ORDER__)&&(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
#define ECHO_LITTLE_ENDIAN
#define ECHO_ENDIAN_DETECTED
#elif defined(__BYTE_ORDER__)&&(__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
#define ECHO_BIG_ENDIAN
#define ECHO_ENDIAN_DETECTED
#else
#error Could not determine platform endian-ness
#endif
#endif //ECHO_ENDIAN_DETECTED
```
You'll need to modify `echo/Types.h` to include a test for your platform then include the appropriate `Types.h` file. For example:
```
//--------------------- Include types for YOURPLATFORM -----------------------
#ifdef ECHO_PLATFORM_YOURPLATFORM
#include <echo/Platforms/YourPlatform/Types.h>
#endif //ECHO_PLATFORM_YOURPLATFORM
```
==Platform namespace==
Contains all of the functions that give access to creating the various platform specific objectsobjects. You should create `src/Platforms/YourPlatform/Platform.cpp` (perhaps copy from an existing) and implement all the required functions as per the header file. These are essentially default objects of the given type for the platform. For example, the `CreateDefaultAudioSystem()` function might create a `OpenALAudio()` object.
You should have a look at the different `Platform.cpp` files for each platform to get a better idea of how some of the implementations are set up. Since the Android environment is quite different to a Linux one you can see how things are set up.
NOTE: On some platforms it might be appropriate to enforce a singleton type instance, but you shouldn't be enforcing your design choice on the user of the platform namespace. Single instances usually are only appropriate when there is a one-to-one relationship with an API or hardware device and that one object controls it. In these cases the one object should be able to deal with use cases where there are essentially two Echo "applications" instances executed in one. This might sound strange, but what you want to consider is that if someone created a Tetris game it should be able to be launched from within an Echo application and have all resources separate to the parent. The general rules is not to enforce any singleton type behaviour though.
==Execution Model==
Implement an `ExecutionModel` class. You also need to return an instance of it in Platform::CreateExecutionModel().
See `ExecutionModel.h` for more documentation on how to properly implement on. You can often copy one from an existing platform that similar execution constraints.
==Timing==
Implement the platform methods for `CPUTimer` in `Platforms/YourPlatform/PlaformTimingLibraryCPUTimer.cpp`
==File System==
- If needed create a `FileSystemSource` compatible with your platform.
Otherwise if your operating system manages a file system for you then you can configure `FileSystemSourceFile`.
Echo specifies that there will be at least a default file system source. That is a source that data is acquired from by default when the source isn't included in a file path.
If your platform supports writing files in some way, you should create a `persistent` FileSystemSource so that an application can save data (configurations, screenshots etc).
==Threading==
Need to either implement:
- The platform methods for `Thread` in `Platforms/YourPlatform/PlaformThreadingLibraryThread.cpp
- The platform methods for `Mutex` in `Platforms/YourPlatform/PlaformThreadingLibraryMutex.cpp
Boost is available as a threading target.
==Graphics==
Need to implement the following:
- A `RenderTarget`
- A `Texture` as `include/echo/Platforms/YourPlatform/PlatformGraphicsLibraryTexture.h` and `src/Platforms/YourPlatform/PlatformGraphicsLibraryTexture.cpp`
Creating a graphics implementation is quite a lot of work. There is an OpenGL implementation available. OpenGLES is available through the OpenGL interface (since they are similar) by defining `ECHO_GLES_SUPPORT` as a pre-processor.
==Audio==
Need to implement the following:
- Audio system
- AudioBuffer (the audio system will create instances of these)
There is an OpenAL implementation that might be suitable for your platform. Keep in mind that OpenAL needs to be dynamically linked to satisfy the license.
If your implementation doesn't support Audio, return a null shared pointer from the Platform function.
==Shell==
Shell implementation is not a requirement.
If you want applications to be able to execute commands via the Shell object you'll need to implement a Shell class.
==Input==
Implement some input devices which should be set up in `Platform::CreateDefaultInputManager()`, typically a `Mouse` and `Keyboard` devices are available on most platforms but this isn't a strict rule.
If you want to allow programmers to be lazy about their device acquisition then create both of these devices, if your platform doesn't support either then you can create a pseudo device for each and update the pseudo devices via some other means. For example a "Mouse" device is currently available for Android which is a `PseudoMouse` that has its position and left button updated on touch events. This isn't a requirement though since it is recommended that application developers utilise a mapped input device for their application so it can be remapped via a configuration file for each of planned targets.
==Network==
Networking isn't a requirement for a platform target but is highly recommended.
Since a socket based networking environment is usually available on platforms there are some classes that aren't in the Platform folder since we haven't come across a platform that we support where this isn't the case. So the socket networking system is implemented in as `SocketNetworkSystem` but can be excluded for a platform if required.
Networking is still abstracted though, the network system is a factory for `Connection` objects. The factory is often configured with a `SocketNetworkSystem` as a default but can also be configured with a custom network implementation.