aboutsummaryrefslogtreecommitdiff
path: root/src/CmakeModule.cpp
blob: e05a65f12d11c2363de411a012210ccc4523f318 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
#include "../include/CmakeModule.hpp"
#include "../include/Exec.hpp"
#include "../include/GlobalLib.hpp"

#if OS_FAMILY == OS_FAMILY_POSIX
#define nprintf printf
#else
#define nprintf wprintf
#endif

using namespace std;

namespace sibs
{
    Result<bool> CmakeModule::compile(const SibsConfig &config, const FileString &buildPath, LinkerFlagCallbackFunc staticLinkerFlagCallbackFunc, LinkerFlagCallbackFunc dynamicLinkerFlagCallbackFunc, GlobalIncludeDirCallbackFunc globalIncludeDirCallback)
    {
        Result<FileString> globalLibDirResult = getHomeDir();
        if (!globalLibDirResult)
            return Result<bool>::Err(globalLibDirResult);
        FileString globalLibDir = globalLibDirResult.unwrap();
        globalLibDir += TINYDIR_STRING("/.sibs/lib");
        Result<bool> createGlobalLibDirResult = createDirectoryRecursive(globalLibDir.c_str());
        if(createGlobalLibDirResult.isErr())
            return createGlobalLibDirResult;
        
        // TODO: Create a cmake module that contains library/include path for the dependencies (https://cmake.org/Wiki/CMake:How_To_Find_Libraries).
        // Modify the project CMakeLists.txt and add: list(APPEND CMAKE_MODULE_PATH "PathToDependenciesCmakeModulesGoesHere").
        // CMakeLists.txt may contain: set(CMAKE_MODULE_PATH "PathToModules"). This needs to be replaced with list append,
        // otherwise our added module path is replaced.
        // It may work to do like vcpkg instead - to use -DCMAKE_TOOLCHAIN_FILE program argument to specify path to script (https://github.com/Microsoft/vcpkg/blob/master/docs/examples/using-sqlite.md)
        for(const Dependency &globalLibDependency : config.getDependencies())
        {
            Result<bool> globalLibLinkerFlagsResult = GlobalLib::getLibsLinkerFlags(config, globalLibDir, globalLibDependency.name, globalLibDependency.version, staticLinkerFlagCallbackFunc, dynamicLinkerFlagCallbackFunc, globalIncludeDirCallback);
            if(globalLibLinkerFlagsResult.isErr())
            {
                if(globalLibLinkerFlagsResult.getErrorCode() == GlobalLib::DependencyError::DEPENDENCY_NOT_FOUND || globalLibLinkerFlagsResult.getErrorCode() == GlobalLib::DependencyError::DEPENDENCY_VERSION_NO_MATCH)
                {
                    printf("Dependency not found in global lib, trying to download from github\n");
                    // TODO: Download several dependencies at the same time by adding them to a list
                    // and then iterate them and download them all using several threads.
                    // All dependecies should be downloaded at the same time, this includes dependencies of dependencies.
                    // If a dependency is missing, fail build BEFORE downloading dependencies and before compiling anything.
                    // You do not want to possibly wait several minutes only for build to fail when there is no compilation error.

                    // TODO: If return error is invalid url, then the message should be converted to
                    // invalid package name/version. A check should be done if it is the name or version
                    // that is invalid.
                    Result<bool> downloadDependencyResult = GlobalLib::downloadDependency(globalLibDependency);
                    if(downloadDependencyResult.isErr())
                        return downloadDependencyResult;

                    globalLibLinkerFlagsResult = GlobalLib::getLibsLinkerFlags(config, globalLibDir, globalLibDependency.name, globalLibDependency.version, staticLinkerFlagCallbackFunc, dynamicLinkerFlagCallbackFunc, globalIncludeDirCallback);
                    if(globalLibLinkerFlagsResult.isErr())
                        return Result<bool>::Err(globalLibLinkerFlagsResult);
                }
                else
                {
                    return Result<bool>::Err(globalLibLinkerFlagsResult);
                }
            }
        }
            
        Result<bool> createBuildDirResult = createDirectoryRecursive(buildPath.c_str());
        if (createBuildDirResult.isErr())
            return createBuildDirResult;
        
        FileString cmd = TINYDIR_STRING("cmake ");
        switch(config.getPackageType())
        {
            case PackageType::EXECUTABLE:
            {
                cmd += config.getCmakeArgs();
                break;
            }
            case PackageType::STATIC:
            {
                cmd += config.getCmakeArgsStatic();
                break;
            }
            case PackageType::DYNAMIC:
            case PackageType::LIBRARY:
            {
                cmd += config.getCmakeArgsDynamic();
                break;
            }
        }
        cmd += TINYDIR_STRING(" \"-B");
        cmd += buildPath;
        cmd += TINYDIR_STRING("\" \"-H");
        switch(config.getPackageType())
        {
            case PackageType::EXECUTABLE:
            {
                cmd += config.getCmakeDir();
                break;
            }
            case PackageType::STATIC:
            {
                cmd += config.getCmakeDirStatic();
                break;
            }
            case PackageType::DYNAMIC:
            case PackageType::LIBRARY:
            {
                cmd += config.getCmakeDirDynamic();
                break;
            }
        }
        cmd += TINYDIR_STRING("\"");
        nprintf("Compiling with cmake with arguments: %s\n", cmd.c_str());
        
        Result<ExecResult> execResult = exec(cmd.c_str(), true);
        if(execResult.isOk())
        {
            if(execResult.unwrap().exitCode != 0)
                return Result<bool>::Err(execResult.unwrap().execStdout);
        }
        else
            return Result<bool>::Err(execResult);
        
        FileString ninjaCommand = TINYDIR_STRING("ninja -C \"");
        ninjaCommand += buildPath;
        ninjaCommand += TINYDIR_STRING("\"");
        nprintf("Compiling cmake generated ninja file: %s\n", ninjaCommand.c_str());
        execResult = exec(ninjaCommand.c_str(), true);
        if(execResult.isOk())
        {
            if(execResult.unwrap().exitCode != 0)
                return Result<bool>::Err(execResult.unwrap().execStdout);
        }
        else
            return Result<bool>::Err(execResult);
        
        if(config.getPackageType() != PackageType::EXECUTABLE)
        {
            FileString libFile;
            u64 libFileLastModified = 0;
            
            string buildPathUtf8 = toUtf8(buildPath);
            nprintf("Searching for library generate by cmake in build path: %s\n", buildPathUtf8.c_str());
            walkDirFiles(buildPath.c_str(), [&libFile, &libFileLastModified](tinydir_file *file)
            {
                if(_tinydir_strcmp(file->extension, CONFIG_DYNAMIC_LIB_FILE_EXTENSION) == 0)
                {
                    u32 fileLastModified = getFileLastModifiedTime(file->path);
                    if(fileLastModified > libFileLastModified)
                    {
                        libFileLastModified = fileLastModified;
                        libFile = file->path;
                    }
                }
            });
            
            if(libFileLastModified == 0)
            {
                string errMsg = "Package ";
                errMsg += config.getPackageName();
                errMsg += " was built using cmake but no generated library was found";
                return Result<bool>::Err(errMsg);
            }
            string libFileUtf8 = toUtf8(libFile);
            nprintf("Library generated by cmake: %s\n", libFileUtf8.c_str());
            
            switch(config.getPackageType())
            {
                case PackageType::STATIC:
                {
                    string libFileCmd = "\"";
                    libFileCmd += libFileUtf8;
                    libFileCmd += "\"";
                    staticLinkerFlagCallbackFunc(libFileCmd);
                    break;
                }
                case PackageType::DYNAMIC:
                case PackageType::LIBRARY:
                {
                    string libFileCmd = "\"";
                    libFileCmd += libFileUtf8;
                    libFileCmd += "\"";
                    dynamicLinkerFlagCallbackFunc(libFileCmd);
                    break;
                }
            }
            
            // TODO: Clean this up. The below code is indentical to code in Ninja.cpp.......
            string projectPathUtf8 = toUtf8(config.getProjectPath());
            for (const string &globalIncludeDir : config.getGlobalIncludeDirs())
            {
                string globalIncludeDirFull = projectPathUtf8;
                globalIncludeDirFull += "/";
                globalIncludeDirFull += globalIncludeDir;
                if(globalIncludeDirCallback)
                    globalIncludeDirCallback(globalIncludeDirFull);
            }
        }
        
        return Result<bool>::Ok(true);
    }
}