Setting up Unreal Engine 4 CMake environment for CLion

This time we’re going to set up our system for later use. I must say that hardware requirements are a bit higher than stated on Unreal Engine website. From my personal point of view I’d recommend having Mac with 16Gb RAM, Core i7 CPU, discrete GPU and SSD drive.

First thing you should know is that you have to build Unreal Engine from source if you want to use any IDE other than Xcode on your Mac, in my case CLion, because CMake file generator is Linux-only and is yet not perfect. Epic team says they have CMake support in development. After registering on UE website you have to follow tutorial and retrieve sources for UE4.

See tutorial for building UE on Mac here.

Let’s try to create simple empty C++ project with name ‘Game’. Wait for project files to be generated. The next step is to review project folder. In project root you will see two project files: `Game.uproject` and `Game.xcodeproject`. But in order to open your game in CLion you need `CMakeLists.txt` file.

You can rebuild project files manually using this command:

cd /path/to/UE4/Engine/Build/BatchFiles/Mac
./GenerateProjectFiles.sh -cmakefile -game -project “/path/to/Game.uproject"

If you look into generated `CMakeLists.txt` you’ll notice that source folders refer to Linux code and build targets also have Linux architecture.

Now, let’s change UnrealBuildTool a bit. Open project at `${UE4_ROOT}/Engine/Source/Programs/UnrealBuildTool/UnrealBuildTool_Mono.csproj` and find `System/CMakefileGenerator.cs`. After exploring module source we see the root of the problem.

We’ll have to determine host architecture and generate `CMakeLists.txt` file according to it.

Add

bool isMac = BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Mac;
bool isLinux = BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Linux;
String HostArchitecture = null;
if (isLinux) HostArchitecture = "Linux";
if (isMac) HostArchitecture = "Mac";

before `if (!String.IsNullOrEmpty (GameProjectName))` in `WriteCMakeLists()` method.

Then replace all “Linux” string entries with “{}” and add HostArchitecture as argument to `String.Format` calls.

Next, move directory filter to separate functions:

private bool IsLinuxFiltered( String SourceFileRelativeToRoot ) {
  return
    !SourceFileRelativeToRoot.Contains ("Source/ThirdParty/") &&
    !SourceFileRelativeToRoot.Contains ("/Windows/") &&
    !SourceFileRelativeToRoot.Contains ("/Mac/") &&
    !SourceFileRelativeToRoot.Contains ("/IOS/") &&

    !SourceFileRelativeToRoot.Contains ("/iOS/") &&

    !SourceFileRelativeToRoot.Contains ("/VisualStudioSourceCodeAccess/") &&

    !SourceFileRelativeToRoot.Contains ("/XCodeSourceCodeAccess/") &&

    !SourceFileRelativeToRoot.Contains ("/WmfMedia/") &&

    !SourceFileRelativeToRoot.Contains ("/IOSDeviceProfileSelector/") &&

    !SourceFileRelativeToRoot.Contains ("/WindowsDeviceProfileSelector/") &&

    !SourceFileRelativeToRoot.Contains ("/WindowsMoviePlayer/") &&

    !SourceFileRelativeToRoot.Contains ("/AppleMoviePlayer/") &&

    !SourceFileRelativeToRoot.Contains ("/MacGraphicsSwitching/") &&

    !SourceFileRelativeToRoot.Contains ("/Apple/") &&

    !SourceFileRelativeToRoot.Contains ("/WinRT/");

}


private bool IsMacFiltered( String SourceFileRelativeToRoot ) {

  return

    !SourceFileRelativeToRoot.Contains ("Source/ThirdParty/") &&

    !SourceFileRelativeToRoot.Contains ("/Windows/") &&

    !SourceFileRelativeToRoot.Contains ("/Linux/") &&

    !SourceFileRelativeToRoot.Contains ("/VisualStudioSourceCodeAccess/") &&

    !SourceFileRelativeToRoot.Contains ("/WmfMedia/") &&

    !SourceFileRelativeToRoot.Contains ("/WindowsDeviceProfileSelector/") &&

    !SourceFileRelativeToRoot.Contains ("/WindowsMoviePlayer/") &&

    !SourceFileRelativeToRoot.Contains ("/WinRT/");
}



And then replace condition on line 109 with

if ((isLinux && IsLinuxFiltered(SourceFileRelativeToRoot)) || (isMac && IsMacFiltered(SourceFileRelativeToRoot)))

Second problem is that there are no include directories, Let’s fix it. Add

private string GetIncludeDirectory(string IncludeDir, string ProjectDir)
{
  string FullProjectPath = Path.GetFullPath(ProjectFileGenerator.MasterProjectRelativePath);
  string FullPath = "";
  if (IncludeDir.StartsWith("/") && !IncludeDir.StartsWith(FullProjectPath))
  {
    // Full path to a folder outside of project
    FullPath = IncludeDir;
  }
  else
  {
    FullPath = Path.GetFullPath(Path.Combine(ProjectDir, IncludeDir));
    FullPath = Utils.MakePathRelativeTo(FullPath, FullProjectPath);
    FullPath = FullPath.TrimEnd('/');
  }
  return FullPath;
}

Finally, print include directories to CMake file.

var IncludeDirectoriesList = "include_directories( \n";
List<String> IncludeDirectories = new List<String>();
foreach (var CurProject in GeneratedProjectFiles) {
  foreach (var CurPath in CurProject.IntelliSenseIncludeSearchPaths) {
    string IncludeDirectory = GetIncludeDirectory(CurPath, Path.GetDirectoryName(CurProject.ProjectFilePath));
    if (IncludeDirectory != null && !IncludeDirectories.Contains (IncludeDirectory)) 
    {
      IncludeDirectories.Add(IncludeDirectory);
    }
  }
}
foreach (string IncludeDirectory in IncludeDirectories) 
{
  IncludeDirectoriesList += ("\t\"" + IncludeDirectory + "\"\n");
}
IncludeDirectoriesList += CMakeSectionEnd;
CMakefileContent.Append (IncludeDirectoriesList);

After all code changes rebuild UnrealBuildProject and run `GenerageProjectFiles.sh` again. You should now have valid CMake file for your Mac, but there if you open the project in CLion and wait for project to parse, you’ll see that most macros are red. Is’s necessary to add preprocessor definitions to CMakeLists.txt file. This is done in the same way as include directories are added.

List<String> PreprocessorDefinitions = new List<String>();
foreach (var CurProject in GeneratedProjectFiles) {
  foreach (var CurPath in CurProject.IntelliSenseIncludeSearchPaths) {
    ... // already added code
  }
  foreach (var CurDefinition in CurProject.IntelliSensePreprocessorDefinitions)
  {
    string Definition = CurDefinition;
    string AlternateDefinition = Definition.Contains("=0") ? Definition.Replace("=0", "=1") : Definition.Replace("=1", "=0");
    if (Definition.Equals("WITH_EDITORONLY_DATA=0") || Definition.Equals("WITH_DATABASE_SUPPORT=1"))
    {
      Definition = AlternateDefinition;
    }
    if (!PreprocessorDefinitions.Contains(Definition) && !PreprocessorDefinitions.Contains(AlternateDefinition) && !Definition.StartsWith("UE_ENGINE_DIRECTORY") && !Definition.StartsWith("ORIGINAL_FILE_NAME"))
    {
      PreprocessorDefinitions.Add(Definition);
    }
  }
}
foreach (string PreprocessorDefinition in PreprocessorDefinitions) 
{
  PreprocessorDefinitionsList += ("\t-D" + PreprocessorDefinition + "\n");
}
PreprocessorDefinitionsList += ("\t-DMONOLITHIC_BUILD=1\n");
PreprocessorDefinitionsList += CMakeSectionEnd;
CMakefileContent.Append (PreprocessorDefinitionsList);

That’s it. Rebuild UnrealBuildTool and generate CMakeLists.txt file again. Now you are ready to start developing your game in CLion.

Please note, that it may happen that includes are not recognised and remain red. This behaviour is known and there is an issue on JetBrains bug tracker. Temporary workaround for this is to create fake build target in CMakeLists.txt

add_executable(FakeTarget $(SOURCE_FILES))
Advertisements

17 thoughts on “Setting up Unreal Engine 4 CMake environment for CLion

  1. Thank you very much for your post, especially the workaround that saved me a lot of time.

    However the reloading in CLion is significantly slow – up to 10min on my machine. But every time a file is added to SOURCES it needs to be reloaded, which is hard to bare. Do you have any solution for this?

    Like

    1. Thank you for your feedback.

      The only solution I see is to comment out all custom_targets in CMakeLists.txt except for two:
      1) Your main game target
      add_custom_target(Game ${BUILD} -project=”\”${GAME_PROJECT_FILE}\”” Game Mac Development $(ARGS) SOURCES ${SOURCE_FILES} ${HEADER_FILES} ${CONFIG_FILES})
      2) The dummy target at the end
      add_executable(Dummy ${SOURCE_FILES})

      CMake project loading time is being reduced dramatically.

      You may want to modify UnrealBuildTool or make some script to do it or comment out targets manually.

      Like

      1. Thanks, it worked for me well.

        But I’ve encountered the following error during my build process. I googled but find nothing appropriate. Have you encountered this before?

        /opt/clion/bin/cmake/bin/cmake --build /home/zh/.clion10/system/cmake/generated/a67d0961/a67d0961/Debug --target labyrinth -- -j 4
        Scanning dependencies of target labyrinth
        Using clang version '3.6.1' (string), 3 (major), 6 (minor), 1 (patch)
        Errors detected while compiling /opt/ue4-git/Engine/Intermediate/Build/BuildRules/-project=ModuleRules.dll:
        (0,0) : error : Unhandled Exception:
        (0,0) : error : System.IO.FileLoadException: I/O Error
        (0,0) : error :   at IKVM.Reflection.AssemblyName..ctor (System.String assemblyName) [0x00000] in :0 
        (0,0) : error :   at Mono.CSharp.AssemblyDefinition.CreateAssemblyName () [0x00000] in :0 
        (0,0) : error :   at Mono.CSharp.AssemblyDefinitionStatic.Create (IKVM.Reflection.Universe domain) [0x00000] in :0 
        (0,0) : error :   at Mono.CSharp.Driver.Compile () [0x00000] in :0 
        (0,0) : error :   at Mono.CSharp.Driver.Main (System.String[] args) [0x00000] in :0 
        (0,0) : error : [ERROR] FATAL UNHANDLED EXCEPTION: System.IO.FileLoadException: I/O Error
        (0,0) : error :   at IKVM.Reflection.AssemblyName..ctor (System.String assemblyName) [0x00000] in :0 
        (0,0) : error :   at Mono.CSharp.AssemblyDefinition.CreateAssemblyName () [0x00000] in :0 
        (0,0) : error :   at Mono.CSharp.AssemblyDefinitionStatic.Create (IKVM.Reflection.Universe domain) [0x00000] in :0 
        (0,0) : error :   at Mono.CSharp.Driver.Compile () [0x00000] in :0 
        (0,0) : error :   at Mono.CSharp.Driver.Main (System.String[] args) [0x00000] in :0 
        

        My possible guess from the log is that it tries to build into my /opt/ where ue4 is installed, so after receiving this error I tried to remove all the Unreal files (10000+lines) from my SOURCE_FILES since I think they should not be compiled again. But it didn’t success.

        So is the unreal sources in the cmake necessary? And do you know how to get it to build?

        Thanks in advance.

        Like

    2. As far as I know, all of them are necessary to build. Anyway, make sure that your /opt/ue4-git/ is writeable and you have all third party software installed and UnrealEngine prebuild.

      As for me, I haven’t encountered that error.

      I hope that helps.

      Like

      1. Hmm. I copied my ue4 installation to my home and created a new project using the copied ue4. This time everything should be writable. But the build process still failed with that error 😦

        “Errors detected while compiling /home/zh/opt/ue4-git/Engine/Intermediate/Build/BuildRules/-project=ModuleRules.dll:”

        Is it trying to find a file named “-project=ModuleRules.dll:”? Maybe I should open a ticket on ue4 for this.

        —-

        But there is another thing that remains confusnig to me. How did you manage to generate the “.generated.h”s? I can’t find out any way for it.

        Like

    3. Speaking about .generated.h’s, they are *surprise, magic happens* generated by UnrealBuildTool and are accessible as soon as compilation finishes successfully.

      And compilation in your case fails. I haven’t encountered this issue but I suppose, that your project misses some configuration. There should be BlankProgramModuleRules.dll, ProjectFileGeneratorModuleRules.dll, UE4EditorModuleRules.dll and UnrealHeaderToolModuleRules.dll with other files in `Intermediate` directory.

      I may only suggest to update sources and rebuild UnrealBuildTool.

      Like

  2. Hi, thanks for this guide! I think your changes were merged into the master branch now, right? I encountered two issue along the way.
    The include_directories and add_definitions sections were empty. Digging through the relevant code, I found this line, which basically seems to prevent these sections from being generated:
    https://github.com/EpicGames/UnrealEngine/blob/4.9/Engine/Source/Programs/UnrealBuildTool/System/CMakefileGenerator.cs#L37
    Did you encounter any issues like this? Setting that value to true fixed the empty sections.
    Then I run into the issue with unrecognized #includes. Using your workaround of adding a fake target solved this as well, but now all the engine files are listed in the project navigator on root level. Any idea how I could hide these? Thanks again!

    Like

  3. Hi! Yes, my changes were merged into the master branch. On the time of the merge there was no ShouldGenerateIntelliSenseData overload in CMakefileGenerator.cs. I look through the sources and yes, changing that boolean field should enable include_directories and add_definitions.

    From my point of view, there should be a command line option to tweak this parameter for CMakeFile because include_directories are only needed for IDE’s support and are ignored if CMakeLists.txt is used only for building. Maybe I’ll do something to resolve this.

    Answering to your second question, no, you can not hide these yet. There is an issue group in CLion bug tracker here: https://youtrack.jetbrains.com/issue/CPP-4284 You may track it or create a subtask. I hope this will be resolved at some time.

    Anyway, you may want to try running UnrealBuildTool with -game key. I’ve noticed that un 4.8.1 UE4 doesn’t add Engine sources to project in come cases. I hope that helps.

    Like

    1. Hi! Would you say this is a CLion issue then? If include_directories is only needed for IDE support, but adding it doesn’t actually help unless you add the fake target? Thanks for the tracker link. I see you already encountered some of these issues. I upvoted the relevant ones.
      I think the missing engine sources were an oversight in 4.8 and fixed in the 4.9 preview.

      Like

  4. Thanks for your post. It help me a lot to get on the road. I wonder if the situation changes today? I grab the Unreal Engine 4.9.2 source code from the official GitHub site, in release channel. Try to to create CLion 1.1.1 project follow your steps. I didn’t modify the .cs project generator file because I think it may already merged into current release. And I see the CMakeLists.txt just created. I open the project, see 10 targets in the project(Game, Game-Mac-Debug, Game-Mac-DebugGame, Game-Mac-Shipping, Game-Mac-Test….). But all of them are not executable so Lion can not build it. And the macro are red in source file. What can I do for now?

    Like

    1. Yes, the 10 targets are created, that is fine. Yet they are not executable, you can build them and it builds fine.

      Also, I have to remark that include_directories and add_definitions are not added due to the bGenerateIntelliSense = false; which appeared after merging my pull request.

      There is a comment below with a pull request that fixes that problem, you may want to do the same locally for the time.

      And do not forget to have a dummy target at the end – it is essential for CLion to work.

      Like

  5. Heya! Wonderful of you to do some work on getting CLion to work with UE4 – I’ve followed it as best I could, and I almost have it working.

    However, I can’t seem to get engine headers to be recognized (the ones in my game work fine), nor can I invoke any of the custom build targets. All of them seem to just try to do a normal cmake build, and aren’t invoking UnrealBuildTool as they should. This is all I get: http://pastebin.com/YcCwznq5

    Any thoughts? If I can get this working, I’ll be back in the saddle in terms of C++ development 😛

    Liked by 1 person

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s