├── unibuild
├── projects
│ ├── __init__.py
│ ├── WixToolkit.py
│ ├── fmtlib.py
│ ├── spdlog.py
│ ├── zlib.py
│ ├── lz4.py
│ ├── asmjit.py
│ ├── googletest.py
│ ├── sevenzip.py
│ ├── udis86.py
│ ├── cygwin.py
│ ├── openssl.py
│ ├── ncc.py
│ ├── sip.py
│ ├── boost.py
│ ├── boostgit.py
│ ├── python.py
│ ├── icu.py
│ ├── pyqt5.py
│ └── qt5.py
├── utility
│ ├── __init__.py
│ ├── singleton.py
│ ├── context_objects.py
│ ├── enum.py
│ ├── format_dict.py
│ ├── progress_file.py
│ ├── case_insensitive_dict.py
│ └── lazy.py
├── __init__.py
├── modules
│ ├── __init__.py
│ ├── googlecode.py
│ ├── sourceforge.py
│ ├── repository.py
│ ├── dummy.py
│ ├── hg.py
│ ├── github.py
│ ├── Patch.py
│ ├── b2.py
│ ├── msbuild.py
│ ├── git.py
│ ├── urldownload.py
│ ├── cmake.py
│ └── build.py
├── dependency.py
├── retrieval.py
├── version.py
├── builder.py
├── progress.py
├── project.py
├── manager.py
└── task.py
├── .gitignore
├── eggs
└── __init__.py
├── README.md
├── config.py
├── libpatterns.py
├── unimake.py
└── makefile.uni.py
/unibuild/projects/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/unibuild/utility/__init__.py:
--------------------------------------------------------------------------------
1 | from format_dict import FormatDict
2 | from progress_file import ProgressFile
3 | from case_insensitive_dict import CIDict
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | build
2 | downloads
3 | *.pyc
4 | progress
5 | Thumbs.db
6 | eggs/*.egg
7 | eggs/decorator
8 | graph.png
9 | .idea
10 | 7za.exe
11 | complete.uni.py
12 |
--------------------------------------------------------------------------------
/unibuild/__init__.py:
--------------------------------------------------------------------------------
1 | from project import Project
2 | from dependency import Dependency
3 | from version import Version
4 | from task import Task
5 | from manager import TaskManager
6 |
--------------------------------------------------------------------------------
/unibuild/modules/__init__.py:
--------------------------------------------------------------------------------
1 | __author__ = 'Tannin'
2 |
3 | import os
4 | import glob
5 |
6 | modules = glob.glob(os.path.join(os.path.dirname(__file__), "*.py"))
7 | __all__ = [os.path.basename(f)[:-3] for f in modules]
--------------------------------------------------------------------------------
/unibuild/utility/singleton.py:
--------------------------------------------------------------------------------
1 | __author__ = 'Tannin'
2 |
3 |
4 | class Singleton(type):
5 | _instances = {}
6 |
7 | def __call__(cls, *args, **kwargs):
8 | if cls not in cls._instances:
9 | cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
10 | return cls._instances[cls]
11 |
--------------------------------------------------------------------------------
/unibuild/dependency.py:
--------------------------------------------------------------------------------
1 | __author__ = 'Tannin'
2 |
3 |
4 | from project import Project
5 |
6 |
7 | class Dependency(Project):
8 |
9 | def __init__(self, name):
10 | super(Dependency, self).__init__(name)
11 |
12 | def applies(self, parameters):
13 | return True
14 |
15 | def version_eq(self, version):
16 | return self
17 |
18 |
--------------------------------------------------------------------------------
/unibuild/modules/googlecode.py:
--------------------------------------------------------------------------------
1 | __author__ = 'Tannin'
2 |
3 |
4 | from urldownload import URLDownload
5 |
6 |
7 | class Release(URLDownload):
8 | def __init__(self, project, filename, tree_depth=0):
9 | super(Release, self)\
10 | .__init__("http://{project}.googlecode.com/files/{filename}".format(project=project,
11 | filename=filename)
12 | , tree_depth)
13 |
--------------------------------------------------------------------------------
/unibuild/modules/sourceforge.py:
--------------------------------------------------------------------------------
1 | __author__ = 'Tannin'
2 |
3 |
4 | from urldownload import URLDownload
5 |
6 |
7 | class Release(URLDownload):
8 | def __init__(self, project, path, tree_depth=0):
9 | super(Release, self)\
10 | .__init__("http://downloads.sourceforge.net/project/{project}/{path}".format(project=project,
11 | path=path),
12 | tree_depth)
13 |
--------------------------------------------------------------------------------
/unibuild/modules/repository.py:
--------------------------------------------------------------------------------
1 | from unibuild.retrieval import Retrieval
2 | from config import config
3 | import os
4 |
5 |
6 | class Repository(Retrieval):
7 | def __init__(self, url, branch):
8 | super(Repository, self).__init__()
9 | self._url = url
10 | self._branch = branch
11 | self._dir_name = os.path.basename(self._url)
12 | self._output_file_path = os.path.join(config["paths"]["build"], self._dir_name)
13 |
14 | @property
15 | def name(self):
16 | return "retrieve {0}".format(self._dir_name)
17 |
18 |
--------------------------------------------------------------------------------
/unibuild/utility/context_objects.py:
--------------------------------------------------------------------------------
1 | from contextlib import contextmanager
2 |
3 |
4 | @contextmanager
5 | def on_failure(func):
6 | """
7 | very generic context object generator that will run a parameterless lambda in case the
8 | wrapped code fails
9 | """
10 | try:
11 | yield
12 | except:
13 | func()
14 | raise
15 |
16 | @contextmanager
17 | def on_exit(func):
18 | """
19 | very generic context object generator that will run a parameterless lambda in case the
20 | wrapped code fails
21 | """
22 | try:
23 | yield
24 | func()
25 | except:
26 | func()
27 | raise
28 |
--------------------------------------------------------------------------------
/unibuild/retrieval.py:
--------------------------------------------------------------------------------
1 | __author__ = 'Tannin'
2 |
3 |
4 | from task import Task
5 | from config import config
6 | import os
7 |
8 |
9 | class Retrieval(Task):
10 |
11 | def __init__(self):
12 | super(Retrieval, self).__init__()
13 | try:
14 | os.makedirs(config["paths"]["download"])
15 | except Exception, e:
16 | # ignore error, probably means the path already exists
17 | pass
18 |
19 | def fulfilled(self):
20 | super(Retrieval, self).fulfilled()
21 |
22 | def applies(self, parameters):
23 | return True
24 |
25 | @property
26 | def name(self):
27 | return
28 |
29 | def process(self, progress):
30 | return
31 |
--------------------------------------------------------------------------------
/unibuild/version.py:
--------------------------------------------------------------------------------
1 | __author__ = 'Tannin'
2 |
3 |
4 | class Version(object):
5 | def __init__(self, version_string):
6 | self.__versionString = version_string
7 |
8 | def __eq__(self, other):
9 | return self.__versionString == other.__versionString
10 |
11 | def __ne__(self, other):
12 | return not self.__eq__(other)
13 |
14 | def __lt__(self, other):
15 | return self.__versionString < other.__versionString
16 |
17 | def __gt__(self, other):
18 | return self.__versionString > other.__versionString
19 |
20 | def __ge__(self, other):
21 | return self.__versionString >= other.__versionString
22 |
23 | def __le__(self, other):
24 | return self.__versionString <= other.__versionString
--------------------------------------------------------------------------------
/unibuild/utility/enum.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2015 Sebastian Herbord. All rights reserved.
2 | #
3 | # This file is part of Mod Organizer.
4 | #
5 | # Mod Organizer is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Mod Organizer is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Mod Organizer. If not, see .
17 |
18 |
19 | def enum(**enums):
20 | return type('Enum', (), enums)
21 |
--------------------------------------------------------------------------------
/unibuild/utility/format_dict.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2015 Sebastian Herbord. All rights reserved.
2 | #
3 | # This file is part of Mod Organizer.
4 | #
5 | # Mod Organizer is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Mod Organizer is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Mod Organizer. If not, see .
17 |
18 |
19 | class FormatDict(dict):
20 | """
21 | a dictionary that doesn't throw an exception on access to an unknown key,
22 | intended to be used for format parameters.
23 | """
24 | def __missing__(self, key):
25 | return "{" + key + "}"
26 |
27 |
--------------------------------------------------------------------------------
/unibuild/builder.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2015 Sebastian Herbord. All rights reserved.
2 | #
3 | # This file is part of Mod Organizer.
4 | #
5 | # Mod Organizer is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Mod Organizer is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Mod Organizer. If not, see .
17 |
18 |
19 | from task import Task
20 |
21 |
22 | class Builder(Task):
23 |
24 | def __init__(self):
25 | super(Builder, self).__init__()
26 |
27 | def applies(self, parameters):
28 | return True
29 |
30 | def name(self):
31 | return
32 |
33 | def process(self, progress):
34 | return
35 |
--------------------------------------------------------------------------------
/unibuild/modules/dummy.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2015 Sebastian Herbord. All rights reserved.
2 | #
3 | # This file is part of Mod Organizer.
4 | #
5 | # Mod Organizer is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Mod Organizer is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Mod Organizer. If not, see .
17 |
18 |
19 | from unibuild import Task
20 |
21 |
22 | class Success(Task):
23 | def __init__(self, name):
24 | super(Success, self).__init__()
25 | self.__name = name
26 |
27 | @property
28 | def name(self):
29 | return "dummy {}".format(self.__name)
30 |
31 | def process(self, progress):
32 | return True
--------------------------------------------------------------------------------
/unibuild/projects/WixToolkit.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2015 Sebastian Herbord. All rights reserved.
2 | #
3 | # This file is part of Mod Organizer.
4 | #
5 | # Mod Organizer is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Mod Organizer is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Mod Organizer. If not, see .
17 |
18 |
19 | from unibuild import Project
20 | from unibuild.modules import github
21 | from config import config
22 | import os
23 |
24 |
25 | WixToolSet_Version_Binary = config['WixToolSet_Version_Binary']
26 |
27 |
28 | Project("WixToolkit") \
29 | .depend(github.Release("wixtoolset", "wix3", "wix{}rtm".format(WixToolSet_Version_Binary),
30 | "wix{}-binaries".format(WixToolSet_Version_Binary))
31 | .set_destination("WixToolkit"))
32 |
--------------------------------------------------------------------------------
/unibuild/projects/fmtlib.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2015 Sebastian Herbord. All rights reserved.
2 | #
3 | # This file is part of Mod Organizer.
4 | #
5 | # Mod Organizer is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Mod Organizer is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Mod Organizer. If not, see .
17 |
18 |
19 | from unibuild import Project
20 | from unibuild.modules import cmake, github
21 | from config import config
22 |
23 | Project("fmtlib") \
24 | .depend(cmake.CMake().arguments(
25 | [
26 | "-DCMAKE_INSTALL_PREFIX:PATH={}".format(config["paths"]["install"].replace('\\', '/')),
27 | "-DCMAKE_BUILD_TYPE={0}".format(config["build_type"]),
28 | ]).install()
29 | .depend(github.Source("fmtlib", "fmt", "3.0.0").set_destination("fmt")
30 | )
31 | )
32 |
--------------------------------------------------------------------------------
/unibuild/utility/progress_file.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2015 Sebastian Herbord. All rights reserved.
2 | #
3 | # This file is part of Mod Organizer.
4 | #
5 | # Mod Organizer is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Mod Organizer is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Mod Organizer. If not, see .
17 |
18 |
19 | import os
20 |
21 |
22 | class ProgressFile(file):
23 |
24 | def __init__(self, filename, progress_cb):
25 | super(ProgressFile, self).__init__(filename, "rb")
26 |
27 | assert callable(progress_cb)
28 | self.__progress_cb = progress_cb
29 | self.seek(0, os.SEEK_END)
30 | self.__size = self.tell()
31 | self.seek(0, os.SEEK_SET)
32 |
33 | def read(self, *args, **kwargs):
34 | self.__progress_cb(self.tell(), self.__size)
35 |
36 | return super(ProgressFile, self).read(*args, **kwargs)
37 |
--------------------------------------------------------------------------------
/unibuild/projects/spdlog.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2015 Sebastian Herbord. All rights reserved.
2 | #
3 | # This file is part of Mod Organizer.
4 | #
5 | # Mod Organizer is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Mod Organizer is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Mod Organizer. If not, see .
17 |
18 | # TODO This is really old, should by updated to 0.13
19 |
20 | from unibuild import Project
21 | from unibuild.modules import cmake, github
22 | from config import config
23 |
24 | Project("spdlog") \
25 | .depend(cmake.CMake().arguments(
26 | [
27 | "-DCMAKE_INSTALL_PREFIX:PATH={}".format(config["paths"]["install"].replace('\\', '/')),
28 | "-DCMAKE_BUILD_TYPE={0}".format(config["build_type"]),
29 | ]).install()
30 | .depend(github.Source("TanninOne", "spdlog", "master").set_destination("spdlog")
31 | )
32 | )
33 |
--------------------------------------------------------------------------------
/unibuild/progress.py:
--------------------------------------------------------------------------------
1 | __author__ = 'Tannin'
2 |
3 |
4 | class Progress(object):
5 |
6 | def __init__(self):
7 | self.__minimum = 0
8 | self.__maximum = 100
9 | self.__value = 0
10 | self.__job = ""
11 | self.__changeCallback = None
12 |
13 | @property
14 | def maximum(self):
15 | return self.__maximum
16 |
17 | @maximum.setter
18 | def maximum(self, new_value):
19 | self.__maximum = new_value
20 |
21 | @property
22 | def minimum(self):
23 | return self.__minimum
24 |
25 | @minimum.setter
26 | def minimum(self, new_value):
27 | self.__minimum = new_value
28 |
29 | @property
30 | def value(self):
31 | return self.__value
32 |
33 | @value.setter
34 | def value(self, new_value):
35 | self.__value = new_value
36 | self.__call_callback()
37 |
38 | @property
39 | def job(self):
40 | return self.__job
41 |
42 | @job.setter
43 | def job(self, new_job):
44 | self.__job = new_job
45 | self.__call_callback()
46 |
47 | def __call_callback(self):
48 | if self.__changeCallback is not None:
49 | self.__changeCallback(self.__job, self.__value * 100 / self.__maximum)
50 |
51 | def finish(self):
52 | self.__changeCallback(None, None)
53 |
54 | def set_change_callback(self, callback):
55 | self.__changeCallback = callback
56 |
--------------------------------------------------------------------------------
/unibuild/projects/zlib.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2015 Sebastian Herbord. All rights reserved.
2 | #
3 | # This file is part of Mod Organizer.
4 | #
5 | # Mod Organizer is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Mod Organizer is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Mod Organizer. If not, see .
17 |
18 |
19 | from unibuild import Project
20 | from unibuild.modules import cmake, urldownload
21 | from config import config
22 | import os
23 |
24 |
25 | zlib_version = config['zlib_version']
26 |
27 |
28 | Project("zlib") \
29 | .depend(cmake.CMake().arguments(["-DCMAKE_BUILD_TYPE={0}".format(config["build_type"]),
30 | "-DCMAKE_INSTALL_PREFIX:PATH={}".format(
31 | os.path.join(config["paths"]["build"], "zlib"))
32 | ]).install()
33 | .depend(urldownload.URLDownload("http://zlib.net/zlib-{}.tar.gz".format(zlib_version), 1)))
34 |
--------------------------------------------------------------------------------
/unibuild/projects/lz4.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2015 Sebastian Herbord. All rights reserved.
2 | #
3 | # This file is part of Mod Organizer.
4 | #
5 | # Mod Organizer is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Mod Organizer is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Mod Organizer. If not, see .
17 |
18 |
19 | from unibuild import Project
20 | from unibuild.modules import github, Patch
21 | from config import config
22 | import os
23 |
24 |
25 | lz4_version = "v1.7.4"
26 |
27 | Project("lz4") \
28 | .depend(Patch.Copy(os.path.join(config['paths']['build'], "lz4", "dll", "liblz4.dll"),
29 | os.path.join(config["paths"]["install"], "bin", "dlls"))
30 | .depend(github.Release("lz4", "lz4", lz4_version, "lz4_{0}_win{1}".format(lz4_version.replace(".","_"),"64" if config['architecture'] == 'x86_64' else "32"),"zip")
31 | .set_destination("lz4")
32 | )
33 | )
34 |
35 |
--------------------------------------------------------------------------------
/unibuild/projects/asmjit.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2015 Sebastian Herbord. All rights reserved.
2 | #
3 | # This file is part of Mod Organizer.
4 | #
5 | # Mod Organizer is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Mod Organizer is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Mod Organizer. If not, see .
17 |
18 |
19 | from unibuild import Project
20 | from unibuild.modules import cmake, github
21 | from config import config
22 |
23 |
24 | # asmjit doesn't currently have any tags/branches but not every commit is usable
25 | asmjit_tag = "master"
26 | asmjit_commit = "fb9f82cb61df36aa513d054e748dc6769045f33e"
27 |
28 | Project("AsmJit") \
29 | .depend(cmake.CMake().arguments(
30 | [
31 | "-DASMJIT_STATIC=TRUE",
32 | "-DASMJIT_DISABLE_COMPILER=TRUE",
33 | "-DCMAKE_INSTALL_PREFIX:PATH={}".format(config["paths"]["install"].replace('\\', '/')),
34 | "-DCMAKE_BUILD_TYPE={0}".format(config["build_type"]),
35 | ]).install()
36 | .depend(github.Source("kobalicek", "asmjit", asmjit_tag, update=False, commit = asmjit_commit)
37 | .set_destination("asmjit"))
38 | )
39 |
40 |
--------------------------------------------------------------------------------
/unibuild/projects/googletest.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2015 Sebastian Herbord. All rights reserved.
2 | #
3 | # This file is part of Mod Organizer.
4 | #
5 | # Mod Organizer is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Mod Organizer is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Mod Organizer. If not, see .
17 |
18 |
19 | from unibuild import Project
20 | from unibuild.modules import cmake, github, build
21 | from config import config
22 | import os
23 | import shutil
24 | import fnmatch
25 |
26 |
27 | googletest_version = "1.8.0"
28 |
29 |
30 | def install(context):
31 | for root, dirnames, filenames in os.walk(os.path.join(context['build_path'], "build")):
32 | for filename in fnmatch.filter(filenames, "*.lib"):
33 | shutil.copy(os.path.join(root, filename), os.path.join(config["paths"]["install"], "libs"))
34 |
35 | return True
36 |
37 |
38 | Project("GTest") \
39 | .depend(build.Execute(install)
40 | .depend(cmake.CMake().arguments(["-Dgtest_force_shared_crt=ON",
41 | "-DCMAKE_BUILD_TYPE={0}".format(config["build_type"])
42 | ])
43 | .depend(github.Source("google", "googletest", "release-{}".format(googletest_version))))
44 | )
45 |
46 |
--------------------------------------------------------------------------------
/eggs/__init__.py:
--------------------------------------------------------------------------------
1 | import os.path
2 | import sys
3 | import urllib2
4 | import pip
5 | import tarfile
6 | from subprocess import call
7 |
8 |
9 | def download(url, filename):
10 | if os.path.exists(filename):
11 | return False
12 |
13 | data = urllib2.urlopen(url)
14 | with open(filename, 'wb') as outfile:
15 | while True:
16 | block = data.read(4096)
17 | if not block:
18 | break
19 | outfile.write(block)
20 | return True
21 |
22 | path = os.path.abspath(os.path.join(os.path.realpath(__file__), os.pardir))
23 |
24 |
25 | for dep in ["https://gitlab.com/LePresidente/python-build-tools/uploads/18a195f7945ca35ad563b428739f254b/buildtools-0.0.2-py2.7.egg"]:
26 | eggpath = os.path.join(path, os.path.basename(dep))
27 | download(dep, eggpath)
28 | sys.path.append(eggpath)
29 |
30 | for dep in ["decorator", "lxml", "PyYAML", "six", "jinja2", "psutil", "patch", "networkx","pydot"]:
31 | destpath = "{0}/{1}".format(path, dep)
32 | if not os.path.exists(destpath):
33 | pip.main(["install", "--target={0}".format(destpath), dep])
34 | sys.path.append(destpath)
35 |
36 | """ neither of these work. particularly building pygraphviz requires a specific VC version in a specific location
37 |
38 |
39 | for dep in ["pygraphviz"]:
40 | pip.main(["install", "--install-option=\"--prefix={}\"".format(path), dep])
41 |
42 |
43 | for dep in ["https://pypi.python.org/packages/source/p/pygraphviz/pygraphviz-1.3.1.tar.gz"]:
44 | basename = os.path.basename(dep)
45 | libpath = os.path.join(path, basename)
46 | if download(dep, libpath):
47 | with tarfile.open(libpath, 'r') as tar:
48 | tar.extractall(path=path)
49 | cwd = os.path.join(path, os.path.splitext(os.path.splitext(basename)[0])[0])
50 | call(["python", "setup.py", "install"], cwd=cwd)
51 | """
52 |
--------------------------------------------------------------------------------
/unibuild/projects/sevenzip.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2015 Sebastian Herbord. All rights reserved.
2 | #
3 | # This file is part of Mod Organizer.
4 | #
5 | # Mod Organizer is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Mod Organizer is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Mod Organizer. If not, see .
17 |
18 |
19 | from unibuild import Project
20 | from unibuild.modules import urldownload, build, Patch
21 | from config import config
22 | import os
23 |
24 |
25 | # newer versions are beta as of now. They have slightly (?) different api as well
26 | sevenzip_version = "16.04"
27 |
28 | # TODO build sevenzip, we require the dll in install/bin/dlls.
29 | # sevenzip is not built here as we only use its source
30 | Project("7zip") \
31 | .depend(Patch.Copy(os.path.join(config['paths']['build'], "7zip", "CPP", "7zip", "Bundles", "Format7zF", "{}"
32 | .format("x86" if config['architecture'] == 'x86' else "AMD64"),"7z.dll"),
33 | os.path.join(config["paths"]["install"], "bin","dlls"))
34 | .depend(build.Run(r"nmake CPU={} NEW_COMPILER=1 MY_STATIC_LINK=1 NO_BUFFEROVERFLOWU=1".format("x86" if config['architecture'] == 'x86' else "AMD64"),
35 | working_directory=os.path.join(config['paths']['build'], "7zip", "CPP", "7zip"))
36 | .depend(urldownload.URLDownload("http://www.7-zip.org/a/7z{}-src.7z".format(sevenzip_version.replace(".", ""))).set_destination("7zip"))))
37 |
--------------------------------------------------------------------------------
/unibuild/modules/hg.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2015 Sebastian Herbord. All rights reserved.
2 | #
3 | # This file is part of Mod Organizer.
4 | #
5 | # Mod Organizer is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Mod Organizer is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Mod Organizer. If not, see .
17 |
18 |
19 | from unibuild.modules.repository import Repository
20 | from subprocess import Popen
21 | from config import config
22 | import os
23 | import logging
24 |
25 |
26 | class Clone(Repository):
27 | def __init__(self, url, branch="default"):
28 | super(Clone, self).__init__(url, branch)
29 |
30 | def prepare(self):
31 | self._context["build_path"] = self._output_file_path
32 |
33 | def process(self, progress):
34 | if os.path.isdir(self._output_file_path):
35 | proc = Popen([config['paths']['hg'], "pull", "-u"],
36 | cwd=self._output_file_path,
37 | env=config["__environment"])
38 | else:
39 | proc = Popen([config['paths']['hg'], "clone", "-b", self._branch,
40 | self._url, self._context["build_path"]],
41 | env=config["__environment"])
42 | proc.communicate()
43 | if proc.returncode != 0:
44 | logging.error("failed to clone repository %s (returncode %s)", self._url, proc.returncode)
45 | return False
46 |
47 | return True
48 |
49 | @staticmethod
50 | def _expiration():
51 | return config.get('repo_update_frequency', 60 * 60 * 24) # default: one day
52 |
53 | def set_destination(self, destination_name):
54 | self._output_file_path = os.path.join(config["paths"]["build"], destination_name)
55 | return self
56 |
--------------------------------------------------------------------------------
/unibuild/project.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2015 Sebastian Herbord. All rights reserved.
2 | #
3 | # This file is part of Mod Organizer.
4 | #
5 | # Mod Organizer is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Mod Organizer is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Mod Organizer. If not, see .
17 |
18 |
19 | from task import Task
20 | from manager import TaskManager, register_project
21 |
22 |
23 | class Project(Task):
24 |
25 | def __init__(self, name):
26 | super(Project, self).__init__()
27 | self.__name = name
28 | self.__context_data = {}
29 | self.__enabled = False
30 | register_project(self)
31 |
32 | @property
33 | def name(self):
34 | return self.__name
35 |
36 | @property
37 | def enabled(self):
38 | return self.__enabled
39 |
40 | @enabled.setter
41 | def enabled(self, value):
42 | self.__enabled = value
43 |
44 | def __getitem__(self, key):
45 | return self.__context_data[key]
46 |
47 | def __setitem__(self, key, value):
48 | self.__context_data[key] = value
49 |
50 | def __contains__(self, keys):
51 | return self.__context_data.__contains__(keys)
52 |
53 | def set_context_item(self, key, value):
54 | self.__context_data[key] = value
55 | return self
56 |
57 | def applies(self, parameters):
58 | return True
59 |
60 | def process(self, progress):
61 | return True
62 |
63 | def depend(self, task):
64 | if type(task) == str:
65 | task_obj = TaskManager().get_task(task)
66 | if task_obj is None:
67 | raise KeyError("unknown project \"{}\"".format(task))
68 | else:
69 | task = task_obj
70 | else:
71 | task.set_context(self)
72 | return super(Project, self).depend(task)
73 |
--------------------------------------------------------------------------------
/unibuild/modules/github.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2015 Sebastian Herbord. All rights reserved.
2 | #
3 | # This file is part of Mod Organizer.
4 | #
5 | # Mod Organizer is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Mod Organizer is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Mod Organizer. If not, see .
17 |
18 |
19 | from urldownload import URLDownload
20 | from git import Clone
21 |
22 |
23 | class Release(URLDownload):
24 | def __init__(self, author, project, version, filename, extension="zip", tree_depth=0):
25 | super(Release, self) \
26 | .__init__("https://github.com/{author}/{project}/releases/download/{version}/"
27 | "{filename}.{extension}".format(author=author,
28 | project=project,
29 | version=version,
30 | filename=filename,
31 | extension=extension),tree_depth)
32 |
33 |
34 | class Source(Clone):
35 | def __init__(self, author, project, branch="master", super_repository=None, update=True, commit=None):
36 | super(Source, self).__init__("https://github.com/{author}/{project}.git".format(author=author,
37 | project=project),
38 | branch, super_repository, update, commit)
39 | #super(Source, self).__init__("https://github.com/{author}/{project}/archive/{tag}.zip".format(), 1)
40 | # don't use the tag as the file name, otherwise we get name collisions on "master" or other generic names
41 | #self.set_destination(project)
42 |
43 |
44 |
45 | # TODO never supported checking out by tag, should create new class here. (required by asmjit)
46 |
--------------------------------------------------------------------------------
/unibuild/manager.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2015 Sebastian Herbord. All rights reserved.
2 | #
3 | # This file is part of Mod Organizer.
4 | #
5 | # Mod Organizer is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Mod Organizer is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Mod Organizer. If not, see .
17 |
18 |
19 | import networkx as nx
20 | from utility.singleton import Singleton
21 |
22 |
23 | class TaskManager(object):
24 | """
25 | manages task dependency graph
26 | """
27 | __metaclass__ = Singleton
28 |
29 | def __init__(self):
30 | self.__topLevelTask = []
31 |
32 | def add_task(self, task):
33 | self.__topLevelTask.append(task)
34 |
35 | def get_task(self, name):
36 | for task in self.__topLevelTask:
37 | if task.name == name:
38 | return task
39 | return None
40 |
41 | def create_graph(self, parameters):
42 | graph = nx.DiGraph()
43 | for task in self.__topLevelTask:
44 | self.__add_task(graph, task, parameters, 0)
45 |
46 | graph.concentrate = True
47 | return graph
48 |
49 | def enable(self, graph, node):
50 | """
51 | recursively enable the node
52 | :param graph:
53 | :param node:
54 | :return:
55 | """
56 | for suc in graph.successors_iter(node):
57 | self.enable(graph, suc)
58 | graph.node[node]["enable"] = True
59 |
60 | def enable_all(self, graph):
61 | for node in graph.nodes_iter():
62 | if graph.in_degree(node) == 0:
63 | self.enable(graph, node)
64 |
65 | def __add_task(self, graph, task, parameters, level):
66 | if not graph.has_node(task.name):
67 | graph.add_node(task.name, color='red' if level == 0 else 'blue', peripheries=max(1, 2 - level),
68 | task=task, enable=False)
69 |
70 | for dependency in task.dependencies:
71 | self.__add_task(graph, dependency, parameters, level + 1)
72 | graph.add_edge(task.name, dependency.name)
73 |
74 |
75 | def register_project(task):
76 | TaskManager().add_task(task)
77 |
--------------------------------------------------------------------------------
/unibuild/projects/udis86.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2015 Sebastian Herbord. All rights reserved.
2 | #
3 | # This file is part of Mod Organizer.
4 | #
5 | # Mod Organizer is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Mod Organizer is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Mod Organizer. If not, see .
17 |
18 |
19 | from unibuild import Project
20 | from unibuild.modules import build, sourceforge
21 | from config import config
22 | from glob import glob
23 | import shutil
24 | import os
25 | import errno
26 |
27 |
28 | udis_version = "1.7"
29 | udis_version_minor = "2"
30 |
31 |
32 | def make_sure_path_exists(path):
33 | try:
34 | os.makedirs(path)
35 | except OSError as exception:
36 | if exception.errno != errno.EEXIST:
37 | raise
38 |
39 |
40 | def install(context):
41 | make_sure_path_exists(os.path.join(config["paths"]["install"], "libs"))
42 | for f in glob(os.path.join(context['build_path'], "*.lib")):
43 | shutil.copy(f, os.path.join(config["paths"]["install"], "libs"))
44 | return True
45 |
46 |
47 | Project("Udis86") \
48 | .depend(build.Execute(install)
49 | .depend((build.CPP().type(build.STATIC_LIB)
50 | .sources("libudis86", ["libudis86/decode.c",
51 | "libudis86/itab.c",
52 | "libudis86/syn.c",
53 | "libudis86/syn-att.c",
54 | "libudis86/syn-intel.c",
55 | "libudis86/udis86.c"])
56 | .custom("libudis86/itab.c",
57 | cmd="{python} scripts/ud_itab.py docs/x86/optable.xml"
58 | " libudis86".format(**config["__environment"]))
59 | )
60 | .depend(sourceforge.Release("udis86", "udis86/{0}/udis86-{0}.{1}.tar.gz".format(udis_version,
61 | udis_version_minor),
62 | tree_depth=1))
63 | )
64 | )
65 |
--------------------------------------------------------------------------------
/unibuild/utility/case_insensitive_dict.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2015 Sebastian Herbord. All rights reserved.
2 | #
3 | # This file is part of Mod Organizer.
4 | #
5 | # Mod Organizer is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Mod Organizer is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Mod Organizer. If not, see .
17 |
18 |
19 | class CIDict(dict):
20 | """
21 | case insensitive dictionary
22 | based on this Stack Overflow post:
23 | http://stackoverflow.com/questions/2082152/case-insensitive-dictionary/32888599
24 | """
25 | def __init__(self, *args, **kwargs):
26 | super(CIDict, self).__init__(*args, **kwargs)
27 | self.__convert_keys()
28 |
29 | def copy(self):
30 | return self.__class__(super(CIDict, self).copy())
31 |
32 | def __getitem__(self, key):
33 | return super(CIDict, self).__getitem__(self.__class__.__key(key))
34 |
35 | def __setitem__(self, key, value):
36 | super(CIDict, self).__setitem__(self.__class__.__key(key), value)
37 |
38 | def __delitem__(self, key):
39 | return super(CIDict, self).__delitem__(self.__class__.__key(key))
40 |
41 | def __contains__(self, key):
42 | return super(CIDict, self).__contains__(self.__class__.__key(key))
43 |
44 | def has_key(self, key):
45 | return super(CIDict, self).__contains__(self.__class__.__key(key))
46 |
47 | def pop(self, key, *args, **kwargs):
48 | return super(CIDict, self).pop(self.__class__.__key(key), *args, **kwargs)
49 |
50 | def get(self, key, *args, **kwargs):
51 | return super(CIDict, self).get(self.__class__.__key(key), *args, **kwargs)
52 |
53 | def setdefault(self, key, *args, **kwargs):
54 | return super(CIDict, self).setdefault(self.__class__.__key(key), *args, **kwargs)
55 |
56 | def update(self, e=None, **f):
57 | super(CIDict, self).update(self.__class__(e))
58 | super(CIDict, self).update(self.__class__(**f))
59 |
60 | @classmethod
61 | def __key(cls, key):
62 | return key.lower() if isinstance(key, basestring) else key
63 |
64 | def __convert_keys(self):
65 | for k in list(self.keys()):
66 | v = super(CIDict, self).pop(k)
67 | self.__setitem__(k, v)
68 |
--------------------------------------------------------------------------------
/unibuild/modules/Patch.py:
--------------------------------------------------------------------------------
1 | from unibuild.task import Task
2 | from unibuild.utility.lazy import Lazy
3 | import os.path
4 | import shutil
5 |
6 |
7 | class Replace(Task):
8 |
9 | def __init__(self, filename, search, substitute):
10 | super(Replace, self).__init__()
11 | self.__file = filename
12 | self.__search = search
13 | self.__substitute = substitute
14 |
15 | @property
16 | def name(self):
17 | return "Replace in {}".format(self.__file)
18 |
19 | def process(self, progress):
20 | full_path = os.path.join(self._context["build_path"], self.__file)
21 | with open(full_path, "r") as f:
22 | data = f.read()
23 |
24 | data = data.replace(self.__search, self.__substitute)
25 |
26 | with open(full_path, "w") as f:
27 | f.write(data)
28 | return True
29 |
30 |
31 | class Copy(Task):
32 |
33 | def __init__(self, source, destination):
34 | super(Copy, self).__init__()
35 | if isinstance(source, str):
36 | source = [source]
37 | self.__source = Lazy(source)
38 | self.__destination = Lazy(destination)
39 |
40 | @property
41 | def name(self):
42 | if self.__source.type() == list:
43 | return "Copy {}...".format(self.__source()[0])
44 | else:
45 | return "Copy {}".format(self.__source.peek())
46 |
47 | def process(self, progress):
48 | if os.path.isabs(self.__destination()):
49 | full_destination = self.__destination()
50 | else:
51 | full_destination = os.path.join(self._context["build_path"], self.__destination())
52 |
53 | for source in self.__source():
54 | if not os.path.isabs(source):
55 | source = os.path.join(self._context["build_path"], source)
56 | if not os.path.exists(full_destination):
57 | os.makedirs(full_destination)
58 | shutil.copy(source, full_destination)
59 | return True
60 |
61 |
62 | class CreateFile(Task):
63 | def __init__(self, filename, content):
64 | super(CreateFile, self).__init__()
65 | self.__filename = filename
66 | self.__content = Lazy(content)
67 |
68 | @property
69 | def name(self):
70 | if self._context is not None:
71 | return "Create File {}-{}".format(self._context.name, self.__filename)
72 | else:
73 | return "Create File {}".format(self.__filename)
74 |
75 | def process(self, progress):
76 | full_path = os.path.join(self._context["build_path"], self.__filename)
77 | with open(full_path, 'w') as f:
78 | # the call to str is necessary to ensure a lazy initialised content is evaluated now
79 | f.write(self.__content())
80 |
81 | return True
82 |
--------------------------------------------------------------------------------
/unibuild/utility/lazy.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2015 Sebastian Herbord. All rights reserved.
2 | #
3 | # This file is part of Mod Organizer.
4 | #
5 | # Mod Organizer is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Mod Organizer is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Mod Organizer. If not, see .
17 |
18 |
19 | class Get(object):
20 | def __init__(self, dictionary, key):
21 | super(Get, self).__init__()
22 | self.__dict = dictionary
23 | self.__key = key
24 |
25 | def __get__(self, instance, owner):
26 | return self.__dict[self.__key]
27 |
28 |
29 | class Evaluate(object):
30 | def __init__(self, func):
31 | super(Evaluate, self).__init__()
32 | self.__data = None
33 | self.__func = func
34 |
35 | def __evaluate(self):
36 | if self.__data is None:
37 | self.__data = self.__func()
38 |
39 | def __getattr__(self, item):
40 | self.__evaluate()
41 | return getattr(self.__data, item)
42 |
43 | def __getitem__(self, item):
44 | self.__evaluate()
45 | return self.__data[item]
46 |
47 | def __str__(self):
48 | self.__evaluate()
49 | return str(self.__data)
50 |
51 | def __len__(self):
52 | if self.__data is not None:
53 | return len(self.__data)
54 | else:
55 | return 0
56 |
57 | def __iter__(self):
58 | self.__evaluate()
59 | return iter(self.__data)
60 |
61 | def __add__(self, other):
62 | self.__evaluate()
63 | return self.__data + other
64 |
65 |
66 | class Lazy(object):
67 | def __init__(self, val):
68 | if callable(val):
69 | self.__value = None
70 | self.__func = val
71 | else:
72 | self.__value = val
73 | self.__func = None
74 |
75 | def __call__(self):
76 | if self.__func is not None:
77 | self.__value = self.__func()
78 | self.__func = None
79 | return self.__value
80 |
81 | def type(self):
82 | if self.__value is None:
83 | return None
84 | else:
85 | return type(self.__value)
86 |
87 | def peek(self):
88 | if self.__value is None:
89 | return self.__func.func_doc
90 | else:
91 | return self.__value
92 |
93 |
94 | def doclambda(func, doc):
95 | func.__doc__ = doc
96 | return func
--------------------------------------------------------------------------------
/unibuild/projects/cygwin.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2015 Sebastian Herbord. All rights reserved.
2 | #
3 | # This file is part of Mod Organizer.
4 | #
5 | # Mod Organizer is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Mod Organizer is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Mod Organizer. If not, see .
17 |
18 |
19 | from unibuild import Project
20 | from unibuild.modules import urldownload, build
21 | from config import config
22 | from subprocess import Popen
23 | import os
24 | import logging
25 | import time
26 | import shutil
27 |
28 |
29 | # installation happens concurrently in separate process. We need to wait for all relevant files to exist,
30 | # and can determine failure only by timeout
31 | timeout = 15 # seconds
32 |
33 |
34 | def bitness():
35 | return "64" if config['architecture'] == "x86_64" else "32"
36 |
37 | filename = "setup-{}.exe".format(config['architecture'])
38 |
39 | url = "http://www.cygwin.com/{}".format(filename)
40 |
41 | Cygwin_Mirror = "http://mirrors.kernel.org/sourceware/cygwin/"
42 |
43 |
44 | def build_func(context):
45 | proc = Popen([os.path.join(config['paths']['download'], filename),
46 | "-q","-C", "Base", "-P", "make,dos2unix,binutils", "-n", "-d", "-O", "-B", "-R", "{}/../cygwin"
47 | .format(context['build_path']), "-l", "{}".format(os.path.join(config['paths']['download'])),
48 | "-s", "{}".format(Cygwin_Mirror)],env=config['__environment'])
49 | proc.communicate()
50 | if proc.returncode != 0:
51 | logging.error("failed to run installer (returncode %s)",
52 | proc.returncode)
53 | return False
54 | dos2unix_path = os.path.join(context['build_path'],"../cygwin","bin", "dos2unix.exe")
55 | make_path = os.path.join(context['build_path'],"../cygwin", "bin", "make.exe")
56 | wait_counter = timeout
57 | while wait_counter > 0:
58 | if os.path.isfile(dos2unix_path) and os.path.isfile(make_path):
59 | break
60 | else:
61 | time.sleep(5.0)
62 | wait_counter -= 5
63 | # wait a bit longer because the installer may have been in the process of writing the file
64 | time.sleep(5.0)
65 |
66 | if wait_counter<=0:
67 | logging.error("Unpacking of Cygwin timed out");
68 | return False #We timed out and nothing was installed
69 |
70 | return True
71 |
72 |
73 | cygwin = Project("cygwin")\
74 | .depend(build.Execute(build_func)
75 | .depend(urldownload.URLDownload(url))
76 | )
77 |
78 |
--------------------------------------------------------------------------------
/unibuild/modules/b2.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2015 Sebastian Herbord. All rights reserved.
2 | #
3 | # This file is part of Mod Organizer.
4 | #
5 | # Mod Organizer is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Mod Organizer is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Mod Organizer. If not, see .
17 |
18 |
19 | from unibuild.builder import Builder
20 | from subprocess import Popen
21 | import os
22 | import logging
23 |
24 |
25 | class B2(Builder):
26 |
27 | def __init__(self,name=None):
28 | super(B2, self).__init__()
29 | self.__arguments = []
30 | self.__name = name
31 |
32 | @property
33 | def name(self):
34 | if self._context is None:
35 | return "b2"
36 | else:
37 | return "b2 {}_{}".format(self._context.name, self.__name)
38 |
39 |
40 |
41 | def applies(self, parameters):
42 | return True
43 |
44 | def fulfilled(self):
45 | return False
46 |
47 | def arguments(self, arguments):
48 | if arguments is None:
49 | self.__arguments = []
50 | else:
51 | self.__arguments = arguments
52 | return self
53 |
54 | def process(self, progress):
55 | if "build_path" not in self._context:
56 | logging.error("source path not known for {},"
57 | " are you missing a matching retrieval script?".format(self.name()))
58 | soutpath = os.path.join(self._context["build_path"], "stdout.log")
59 | serrpath = os.path.join(self._context["build_path"], "stderr.log")
60 | with open(soutpath, "a") as sout:
61 | with open(serrpath, "a") as serr:
62 | proc = Popen(["cmd.exe", "/C", "bootstrap.bat"], cwd=self._context["build_path"],
63 | stdout=sout, stderr=serr)
64 | proc.communicate()
65 | if proc.returncode != 0:
66 | logging.error("failed to bootstrap (returncode %s), see %s and %s",
67 | proc.returncode, soutpath, serrpath)
68 | return False
69 |
70 | cmdline = ["b2.exe"]
71 | if self.__arguments:
72 | cmdline.extend(self.__arguments)
73 |
74 | proc = Popen(cmdline, cwd=self._context["build_path"], stdout=sout, stderr=serr, shell=True)
75 | proc.communicate()
76 | if proc.returncode != 0:
77 | logging.error("failed to build (returncode %s), see %s and %s",
78 | proc.returncode, soutpath, serrpath)
79 | return False
80 | return True
81 |
--------------------------------------------------------------------------------
/unibuild/projects/openssl.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2015 Sebastian Herbord. All rights reserved.
2 | #
3 | # This file is part of Mod Organizer.
4 | #
5 | # Mod Organizer is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Mod Organizer is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Mod Organizer. If not, see .
17 |
18 |
19 | from unibuild import Project
20 | from unibuild.modules import urldownload, build, Patch
21 | from config import config
22 | from subprocess import Popen
23 | import os
24 | import logging
25 | import time
26 | import shutil
27 |
28 |
29 | # currently binary installation only
30 |
31 |
32 | openssl_version = config['openssl_version']
33 |
34 | libeay = "libeay32MD.lib"
35 | ssleay = "ssleay32MD.lib"
36 | # installation happens concurrently in separate process. We need to wait for all relevant files to exist,
37 | # and can determine failure only by timeout
38 | timeout = 15 # seconds
39 |
40 |
41 | def bitness():
42 | return "64" if config['architecture'] == "x86_64" else "32"
43 |
44 | filename = "Win{}OpenSSL-{}.exe".format(bitness(), openssl_version.replace(".", "_"))
45 |
46 | url = "https://slproweb.com/download/{}".format(filename)
47 |
48 |
49 | def build_func(context):
50 | proc = Popen([os.path.join(config['paths']['download'], filename),
51 | "/VERYSILENT", "/DIR={}".format(context['build_path'])],
52 | env=config['__environment'])
53 | proc.communicate()
54 | if proc.returncode != 0:
55 | logging.error("failed to run installer (returncode %s)",
56 | proc.returncode)
57 | return False
58 | libeay_path = os.path.join(context['build_path'], "lib", "VC", "static", libeay)
59 | ssleay_path = os.path.join(context['build_path'], "lib", "VC", "static", ssleay)
60 | wait_counter = timeout
61 | while wait_counter > 0:
62 | if os.path.isfile(libeay_path) and os.path.isfile(ssleay_path):
63 | break
64 | else:
65 | time.sleep(1.0)
66 | wait_counter -= 1
67 | # wait a bit longer because the installer may have been in the process of writing the file
68 | time.sleep(1.0)
69 |
70 | if wait_counter<=0:
71 | logging.error("Unpacking of OpenSSL timed out");
72 | return False #We timed out and nothing was installed
73 |
74 | return True
75 |
76 |
77 | openssl = Project("openssl") \
78 | .depend(Patch.Copy([os.path.join(config['paths']['build'], "Win{0}OpenSSL-{1}"
79 | .format("32" if config['architecture'] == 'x86' else "64",
80 | openssl_version.replace(".","_")), "ssleay32.dll"),
81 | os.path.join(config['paths']['build'], "Win{0}OpenSSL-{1}"
82 | .format("32" if config['architecture'] == 'x86' else "64",
83 | openssl_version.replace(".", "_")), "libeay32.dll"), ],
84 | os.path.join(config["paths"]["install"], "bin","dlls"))
85 | .depend(build.Execute(build_func)
86 | .depend(urldownload.URLDownload(url))
87 | ))
88 |
89 |
--------------------------------------------------------------------------------
/unibuild/modules/msbuild.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2015 Sebastian Herbord. All rights reserved.
2 | #
3 | # This file is part of Mod Organizer.
4 | #
5 | # Mod Organizer is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Mod Organizer is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Mod Organizer. If not, see .
17 |
18 |
19 | from build import Builder
20 | from subprocess import Popen
21 | from config import config
22 | import shutil
23 | import logging
24 | import os
25 |
26 |
27 | class MSBuild(Builder):
28 |
29 | def __init__(self, solution, project=None, working_directory=None, project_platform=None, project_PlatformToolset=None ):
30 | super(MSBuild, self).__init__()
31 | self.__solution = solution
32 | self.__project = project
33 | self.__working_directory = working_directory
34 | self.__project_platform = project_platform
35 | self.__project_platformtoolset = project_PlatformToolset
36 |
37 | @property
38 | def name(self):
39 | if self._context is None:
40 | return "msbuild"
41 | else:
42 | return "msbuild {0}".format(self._context.name)
43 |
44 | def applies(self, parameters):
45 | return True
46 |
47 | def fulfilled(self):
48 | return False
49 |
50 | def process(self, progress):
51 | if "build_path" not in self._context:
52 | logging.error("source path not known for {},"
53 | " are you missing a matching retrieval script?".format(self._context.name))
54 | return False
55 |
56 | soutpath = os.path.join(self._context["build_path"], "stdout.log")
57 | serrpath = os.path.join(self._context["build_path"], "stderr.log")
58 |
59 | with open(soutpath, "w") as sout:
60 | with open(serrpath, "w") as serr:
61 | args = ["msbuild", self.__solution, "/m", "/property:Configuration=Release"]
62 |
63 | if self.__project_platform is None:
64 | args.append("/property:Platform={}"
65 | .format("x64" if config['architecture'] == 'x86_64' else "win32"))
66 | else:
67 | args.append("/property:Platform={}".format(self.__project_platform))
68 |
69 | if self.__project_platformtoolset is not None:
70 | args.append("/property:PlatformToolset={}"
71 | .format(self.__project_platformtoolset))
72 |
73 | if self.__project:
74 | args.append("/target:{}".format(self.__project))
75 |
76 | proc = Popen(
77 | args,
78 | shell=True,
79 | cwd=str(self.__working_directory or self._context["build_path"]),
80 | env=config["__environment"],
81 | stdout=sout, stderr=serr)
82 | proc.communicate()
83 | if proc.returncode != 0:
84 | logging.error("failed to generate makefile (returncode %s), see %s and %s",
85 | proc.returncode, soutpath, serrpath)
86 | return False
87 |
88 | return True
89 |
--------------------------------------------------------------------------------
/unibuild/projects/ncc.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2015 Sebastian Herbord. All rights reserved.
2 | #
3 | # This file is part of Mod Organizer.
4 | #
5 | # Mod Organizer is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Mod Organizer is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Mod Organizer. If not, see .
17 |
18 | import eggs
19 | from unibuild import Project
20 | from unibuild.modules import build, msbuild, Patch, github
21 | from unibuild.utility import lazy
22 | from config import config
23 | import os
24 | from buildtools import log
25 | from buildtools.buildsystem.visualstudio import (ProjectType,
26 | VisualStudio2015Solution,
27 | VS2015Project)
28 |
29 |
30 | def prepare_nmm(context):
31 | sln = VisualStudio2015Solution()
32 | sln.LoadFromFile(os.path.join(context['build_path'],'NexusClient.sln'))
33 | ncc_csproj = os.path.join(context['build_path'],"..",'NexusClientCLI', 'NexusClientCLI', 'NexusClientCLI.csproj')
34 | if not os.path.isfile(ncc_csproj):
35 | log.critical('NOT FOUND: %s', ncc_csproj)
36 | else:
37 | log.info('FOUND: %s', ncc_csproj)
38 | changed = False
39 | projfile = VS2015Project()
40 | projfile.LoadFromFile(ncc_csproj)
41 | projguid = projfile.PropertyGroups[0].element.find('ProjectGuid').text
42 | log.info('ProjectGuid = %s', projguid)
43 | if "NexusClientCli" not in sln.projectsByName:
44 | newproj = sln.AddProject('NexusClientCli', ProjectType.CSHARP_PROJECT, ncc_csproj, guid=projguid)
45 | log.info('Adding project %s (%s) to NexusClient.sln', newproj.name, newproj.guid)
46 | changed = True
47 | else:
48 | newproj = sln.projectsByName['NexusClientCli']
49 | log.info('Project %s (%s) already exists in NexusClient.sln', newproj.name, newproj.guid)
50 | if newproj.projectfile != ncc_csproj:
51 | log.info('Changing projectfile: %s -> %s', newproj.projectfile, ncc_csproj)
52 | newproj.projectfile = ncc_csproj
53 | changed = True
54 | if changed:
55 | log.info('Writing NexusClientCli.sln')
56 | sln.SaveToFile(os.path.relpath(os.path.join(ncc['build_path'],"..","nmm",'NexusClientCli.sln'))) # So we don't get conflicts when pulling.
57 | return True
58 |
59 |
60 | init_repos = github.Source("Nexus-Mods", "Nexus-Mod-Manager", "master") \
61 | .set_destination(os.path.join("NCC", "nmm"))
62 |
63 |
64 | ncc = Project("NCC") \
65 | .depend(build.Run(r"publish.bat"
66 | .format("-debug" if config['build_type'] == "Debug" else "-release",
67 | os.path.join(config["paths"]["install"], "bin")),
68 | working_directory=lazy.Evaluate(lambda: os.path.join(ncc['build_path'], "..", "NexusClientCli")))
69 | .depend(msbuild.MSBuild(os.path.join(config['paths']['build'],"NCC","nmm",'NexusClientCli.sln'),
70 | working_directory=lazy.Evaluate(lambda: os.path.join(ncc['build_path'], "..", "nmm")),project_platform="Any CPU")
71 | .depend(build.Execute(prepare_nmm, name="append NexusClientCli project to NMM")
72 |
73 | .depend(init_repos).depend(github.Source(config['Main_Author'], "modorganizer-NCC", "master") \
74 | .set_destination(os.path.join("NCC", "NexusClientCli"))
75 | ))))
76 |
77 |
78 |
79 |
80 |
--------------------------------------------------------------------------------
/unibuild/projects/sip.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2015 Sebastian Herbord. All rights reserved.
2 | #
3 | # This file is part of Mod Organizer.
4 | #
5 | # Mod Organizer is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Mod Organizer is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Mod Organizer. If not, see .
17 |
18 |
19 | from unibuild import Project
20 | from unibuild.modules import sourceforge, build
21 | from subprocess import Popen
22 | from config import config
23 | from glob import glob
24 | import os
25 | import logging
26 | import python
27 | import errno
28 | import shutil
29 |
30 |
31 | sip_version = "4.19.1"
32 | python_version = config.get('python_version', "2.7") + config.get('python_version_minor', ".13")
33 |
34 |
35 | def sip_environment():
36 | result = config['__environment'].copy()
37 | result['LIB'] += os.path.join(config['paths']['build'],"python-{}".format(python_version),"PCbuild","amd64")
38 | return result
39 |
40 |
41 | def make_sure_path_exists(path):
42 | try:
43 | os.makedirs(path)
44 | except OSError as exception:
45 | if exception.errno != errno.EEXIST:
46 | raise
47 |
48 |
49 | def copy_pyd(context):
50 | make_sure_path_exists(os.path.join(config["__build_base_path"], "install", "bin", "plugins", "data"))
51 | for f in glob(os.path.join(python.python['build_path'], "Lib", "site-packages", "sip.pyd")):
52 | shutil.copy(f, os.path.join(config["__build_base_path"], "install", "bin", "plugins", "data"))
53 | return True
54 |
55 |
56 | class SipConfigure(build.Builder):
57 | def __init__(self):
58 | super(SipConfigure, self).__init__()
59 |
60 | @property
61 | def name(self):
62 | return "sip configure"
63 |
64 | def process(self, progress):
65 | soutpath = os.path.join(self._context["build_path"], "stdout.log")
66 | serrpath = os.path.join(self._context["build_path"], "stderr.log")
67 | with open(soutpath, "w") as sout:
68 | with open(serrpath, "w") as serr:
69 | bp = python.python['build_path']
70 |
71 | proc = Popen([os.path.join(python.python['build_path'],"PCbuild","amd64","python.exe"), "configure.py",
72 | "-b", bp,
73 | "-d", os.path.join(bp, "Lib", "site-packages"),
74 | "-v", os.path.join(bp, "sip"),
75 | "-e", os.path.join(bp, "include")
76 | ],
77 | env=config["__environment"],
78 | cwd=self._context["build_path"],
79 | shell=True,
80 | stdout=sout, stderr=serr)
81 | proc.communicate()
82 | if proc.returncode != 0:
83 | logging.error("failed to run sip configure.py (returncode %s), see %s and %s",
84 | proc.returncode, soutpath, serrpath)
85 | return False
86 |
87 | return True
88 |
89 |
90 | Project('sip') \
91 | .depend(build.Execute(copy_pyd)
92 | .depend(build.Make(environment=sip_environment()).install()
93 | .depend(SipConfigure()
94 | .depend("Python")
95 | .depend(sourceforge.Release("pyqt",
96 | "sip/sip-{0}/sip-{0}.zip".format(sip_version),
97 | 1)
98 | )
99 | )
100 | )
101 | )
102 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # modorganizer-umbrella
2 | An umbrella- (super-) project for modorganizer.
3 |
4 | ##Build instructions for all required Components
5 |
6 | Build Instructions:
7 | unimake.py -d "F:\Build"
8 |
9 | ## Purpose
10 | This repository contains a meta build-system that is able to download and build MO subprojects and dependencies as necessary.
11 | It can be used to build the whole project to produce a build that should be equivalent to the release or to build subprojects (i.e. plugins) with the minimum of dependencies.
12 |
13 | This umbrella project can optionally produce ide projects for developers.
14 |
15 | ## Notes
16 | * While mostly functional this project is work in progress and should only be used by those with the will to spend some time.
17 | * Currently all dependencies are built from source, including monsters like Qt and python. This is necessary to produce releasable bundles (pre-built python would introduce additional run-time dependencies, pre-built Qt doesn't provide debug symbols (pdbs)) but it is overkill if all you want to do is contribute to a plugin.
18 |
19 | ## Concept
20 | At its base this is actually a fairly simple program. Arbitrary command calls are wrapped inside Task objects (grouped as projects) and put into a dependency graph.
21 | The tasks are then executed in proper order, with the umbrella providing the environment so you don't need to have all required tool in your global PATH variable.
22 |
23 | There are specialised task implementations to conveniently specify sources to be retrieved from a repository or to get the correct make tool invoked.
24 |
25 | Now one thing that may be a bit confusing is that all tasks have to be fully initialized before processing starts but since tasks will usually build upon each other, not all information may be available at that time.
26 | In these cases functions/lambdas can be passed as parameters in task initialization which will then be invoked when that task is processed which will be after all dependencies are complete.
27 |
28 | Some more details:
29 | - Successfully completed tasks are memorized (in the "progress" directory) and will not be run again
30 | - Names for tasks are generated so they may not be very user-friendly
31 | - Technically, independent tasks could be executed in parallel but that is not (yet) implemented
32 |
33 | ## Open Problems
34 |
35 | While conceptually this isn't particularly complicated, the actual build process for some tools are massively complex. Some issues I have not been able to work around yet:
36 | - I can't seem to "make install" the qt webkit subproject properly. After building I have to manually move around files to impossible locations (#include "../../../../Source/and/so/on"-the f???)
37 | - The windows variant of grep depends on dlls and the way the qt build system calls it they apparently can't be found even if they reside in the same directory as the exe.
38 | - If openssl fails to install. Make sure you either click yes to the UAC message, or disable UAC in windows
39 |
40 | ## Dependencies
41 |
42 | Windows 7 and up (64bit)
43 |
44 | All the following need to be either on your PATH, or available from Program Files, Program Files (x86), C:\ or D:\. Note that apart from ruby, these things install themselves in Program Files or Program Files (x86) by default so ruby is the only one you might need to be careful about.
45 |
46 | * python 2.7 (2.7.13 x64)
47 | * decorator
48 | * visual Studio 2015 (Including VC Common tools)
49 |
50 | * cmake
51 | * 7zip - specifically, the command line version (7za)
52 | * svn (SlinkSVN)
53 | * ruby (v2.2 x64)
54 | * git
55 | * perl (Strawberry Perl)
56 |
57 | ## Usage
58 |
59 | ```
60 | usage: unimake.py [-h] [-f FILE] [-d DESTINATION] [target [target ...]]
61 |
62 | positional arguments:
63 | target make target
64 |
65 | optional arguments:
66 | -h, --help show this help message and exit
67 | -f FILE, --file FILE sets the build script
68 | -d DESTINATION, --destination DESTINATION
69 | output directory (base for download and build)
70 | -s config_option_name=value, --set config_option_name=value change a config option
71 |
72 | I'd suggest to use a destination folder that isn't too deep, some dependencies don't handle long paths well.
73 | If the make target is left empty, everything is built.
74 |
--------------------------------------------------------------------------------
/unibuild/task.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2015 Sebastian Herbord. All rights reserved.
2 | #
3 | # This file is part of Mod Organizer.
4 | #
5 | # Mod Organizer is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Mod Organizer is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Mod Organizer. If not, see .
17 |
18 |
19 | from manager import TaskManager
20 | import os.path
21 | import time
22 | from config import config
23 |
24 |
25 | class Task(object):
26 | """
27 | base class of all elements in the dependency graph
28 | """
29 |
30 | class FailBehaviour:
31 | FAIL = 1
32 | CONTINUE = 2
33 | SKIP_PROJECT = 3
34 |
35 | def __init__(self):
36 | self.__dependencies = []
37 | self._context = None
38 | self.__fail_behaviour = Task.FailBehaviour.FAIL
39 |
40 | @property
41 | def name(self):
42 | return
43 |
44 | @property
45 | def settings(self):
46 | return {}
47 |
48 | @property
49 | def dependencies(self):
50 | return self.__dependencies
51 |
52 | @property
53 | def enabled(self):
54 | return True
55 |
56 | @enabled.setter
57 | def enabled(self, value):
58 | pass
59 |
60 | @property
61 | def fail_behaviour(self):
62 | return self.__fail_behaviour
63 |
64 | def set_fail_behaviour(self, behaviour):
65 | self.__fail_behaviour = behaviour
66 | return self
67 |
68 | @staticmethod
69 | def _expiration():
70 | return None
71 |
72 | def __success_path(self):
73 | task_name = self.name.replace("/", "_").replace("\\", "_")
74 | ctx_name = self._context.name if self._context else task_name
75 | return os.path.join(config["paths"]["progress"],
76 | "{}_complete_{}.txt".format(ctx_name, task_name))
77 |
78 | def already_processed(self):
79 | if not os.path.exists(self.__success_path()):
80 | return False
81 |
82 | expiration_duration = self._expiration()
83 | if expiration_duration:
84 | return os.path.getmtime(self.__success_path()) + expiration_duration > time.time()
85 | else:
86 | return True
87 |
88 | def mark_success(self):
89 | with open(self.__success_path(), "w"):
90 | pass
91 |
92 | def depend(self, task):
93 | """
94 | add a task as a dependency of this one. This means that the dependent task has to be fulfilled
95 | before this one will be processed.
96 | The order in which dependencies are fulfilled is arbitrary however, you can not control which
97 | of two sibling Tasks is processed first. This is because independent tasks could be processed
98 | asynchronously and they may be also be dependencies for a third task.
99 | """
100 | if type(task) == str:
101 | task_obj = TaskManager().get_task(task)
102 | if task_obj is None:
103 | raise KeyError("unknown project \"{}\"".format(task))
104 | else:
105 | task = task_obj
106 | else:
107 | if self._context:
108 | task.set_context(self._context)
109 |
110 | self.__dependencies.append(task)
111 | return self
112 |
113 | def set_context(self, context):
114 | if self._context is None:
115 | self._context = context
116 | for dep in self.__dependencies:
117 | dep.set_context(context)
118 |
119 | def applies(self, parameters):
120 | return
121 |
122 | def fulfilled(self):
123 | for dep in self.__dependencies:
124 | if not dep.fulfilled():
125 | return False
126 | return True
127 |
128 | def prepare(self):
129 | """
130 | initialize this task. At this point you can rely on required tasks to have run. This should be quick to
131 | complete but needs to initialize everything required by dependent tasks (globals, config, context).
132 | unlike progress, this is called if the task ran successfully already
133 | """
134 | pass
135 |
136 | def process(self, progress):
137 | pass
138 |
--------------------------------------------------------------------------------
/unibuild/projects/boost.py:
--------------------------------------------------------------------------------
1 | from unibuild import Project
2 | from unibuild.modules import b2, sourceforge, Patch, build
3 | from unibuild.projects import python
4 | from config import config
5 | import os
6 | import patch
7 |
8 |
9 | boost_version = config["boost_version"]
10 | python_version = config["python_version"]
11 | vc_version = config['vc_version_for_boost']
12 |
13 | boost_components = [
14 | "date_time",
15 | "coroutine",
16 | "filesystem",
17 | "python",
18 | "thread",
19 | "log",
20 | "locale"
21 | ]
22 |
23 |
24 | config_template = ("using python\n"
25 | " : {0}\n"
26 | " : {1}/python.exe\n"
27 | " : {2}/Include\n"
28 | " : {1}\n"
29 | " : {3}\n"
30 | " : BOOST_ALL_NO_LIB=1\n"
31 | " ;")
32 |
33 |
34 | def patchboost(context):
35 | try:
36 | savedpath = os.getcwd()
37 | os.chdir(os.path.join("{}/boost_{}".format(config["paths"]["build"], config["boost_version"].replace(".", "_"))))
38 | pset = patch.fromfile(os.path.join(config["paths"]["build"], "usvfs", "patches", "type_traits_vs15_fix.patch"))
39 | pset.apply()
40 | os.chdir(savedpath)
41 | return True
42 | except OSError:
43 | return False
44 |
45 | Project("boost") \
46 | .depend(Patch.Copy(os.path.join("{}/boost_{}/stage/lib/boost_python-vc{}-mt-{}.dll"
47 | .format(config["paths"]["build"],
48 | config["boost_version"].replace(".", "_"),
49 | vc_version.replace(".",""),
50 | "_".join(boost_version.split(".")[:-1])
51 | )),
52 | os.path.join(config["paths"]["install"], "bin"))
53 | .depend(b2.B2(name="Shared").arguments(["address-model={}".format("64" if config['architecture'] == 'x86_64' else "32"),
54 | "-a",
55 | "--user-config={}".format(os.path.join(config['paths']['build'],
56 | "boost_{}".format(boost_version.
57 | replace(".", "_")),
58 | "user-config.jam")),
59 | "-j {}".format(config['num_jobs']),
60 | "toolset=msvc-" + vc_version,
61 | "link=shared",
62 | ] + ["--with-{0}".format(component) for component in boost_components])
63 | .depend(b2.B2(name="Static").arguments(["address-model={}".format("64" if config['architecture'] == 'x86_64' else "32"),
64 | "-a",
65 | "--user-config={}".format(os.path.join(config['paths']['build'],
66 | "boost_{}".format(boost_version.
67 | replace(".", "_")), "user-config.jam")),
68 | "-j {}".format(config['num_jobs']),
69 | "toolset=msvc-" + vc_version,
70 | "link=static",
71 | "runtime-link=shared",
72 | ] + ["--with-{0}".format(component) for component in boost_components])
73 | .depend(Patch.CreateFile("user-config.jam",
74 | lambda: config_template.format(
75 | python_version,
76 | os.path.join(python.python['build_path'], "PCBuild",
77 | "{}".format("" if config['architecture'] == 'x86' else "amd64")).replace("\\",'/'),
78 | os.path.join(python.python['build_path']).replace("\\",'/'),
79 | "64" if config['architecture'] == "x86_64" else "32")
80 | ).depend(build.Execute(patchboost)
81 | .depend(sourceforge.Release("boost",
82 | "boost/{0}/boost_{1}.tar.bz2".format(boost_version,
83 | boost_version.replace(".", "_")),
84 | tree_depth=1))
85 | ).depend("Python")
86 | )
87 | )
88 | )
89 | )
90 |
--------------------------------------------------------------------------------
/unibuild/projects/boostgit.py:
--------------------------------------------------------------------------------
1 | from unibuild import Project, Task
2 | from unibuild.modules import b2, git, Patch, build
3 | from unibuild.projects import python
4 | from config import config
5 | import os
6 |
7 |
8 | boost_version = config["boost_version"]
9 | python_version = config["python_version"]
10 | python_version_minor = config["python_version_minor"]
11 | vc_version = config['vc_version_for_boost']
12 | python_path = os.path.join(config['paths']['build'], "python-{}{}".format(python_version, python_version_minor))
13 |
14 | boost_components = [
15 | "date_time",
16 | "coroutine",
17 | "filesystem",
18 | "python",
19 | "thread",
20 | "log",
21 | "locale"
22 | ]
23 |
24 |
25 | config_template = ("using python\n"
26 | " : {0}\n"
27 | " : {1}/python.exe\n"
28 | " : {2}/Include\n"
29 | " : {1}\n"
30 | " : {3}\n"
31 | " : BOOST_ALL_NO_LIB=1\n"
32 | " ;")
33 |
34 | init_repo = build.Run("git submodule init && git submodule update", name="init boost repository" ,working_directory=lambda: os.path.join(config["paths"]["build"], "boost_git")) \
35 | .set_fail_behaviour(Task.FailBehaviour.CONTINUE) \
36 | .depend(git.Clone("https://github.com/boostorg/boost.git", "develop").set_destination("boost_git"))
37 |
38 | Project("boostgit") \
39 | .depend(b2.B2(name="Shared").arguments(["address-model={}".format("64" if config['architecture'] == 'x86_64' else "32"),
40 | "-a",
41 | "--user-config={}".format(os.path.join(config['paths']['build'],
42 | "boost_git",
43 | "user-config.jam")),
44 | "-j {}".format(config['num_jobs']),
45 |
46 | "toolset=msvc-" + vc_version,
47 |
48 | "link=shared",
49 | "include={}".format(os.path.join(config['paths']['build'], "icu", "dist", "include", "unicode")),
50 | "-sICU_PATH={}".format(
51 | os.path.join(config['paths']['build'], "icu", "dist")),
52 | "-sHAVE_ICU=1",
53 | ] + ["--with-{0}".format(component) for component in boost_components])
54 | .depend(b2.B2(name="Static").arguments(["address-model={}".format("64" if config['architecture'] == 'x86_64' else "32"),
55 | "-a",
56 | "--user-config={}".format(os.path.join(config['paths']['build'],
57 | "boost_git", "user-config.jam")),
58 | "-j {}".format(config['num_jobs']),
59 |
60 | "toolset=msvc-" + vc_version,
61 |
62 | "link=static",
63 | "runtime-link=shared",
64 | "include={}".format(os.path.join(config['paths']['build'], "icu", "dist", "include", "unicode")),
65 | "-sICU_PATH={}".format(os.path.join(config['paths']['build'], "icu", "dist")),
66 | "-sHAVE_ICU=1",
67 | ] + ["--with-{0}".format(component) for component in boost_components])
68 | .depend(build.Run(r"bootstrap.bat",working_directory=lambda: os.path.join(config["paths"]["build"], "boost_git")))
69 | .depend(Patch.CreateFile("user-config.jam",
70 | lambda: config_template.format(
71 | python_version,
72 | os.path.join(python_path, "PCBuild",
73 | "{}".format("" if config['architecture'] == 'x86' else "amd64")).replace("\\",'/'),
74 | python_path,
75 | "64" if config['architecture'] == "x86_64" else "32")
76 | ))
77 | .depend(init_repo)
78 | # .depend(sourceforge.Release("boost",
79 | # "boost/{0}/boost_{1}.tar.bz2".format(boost_version,
80 | # boost_version.replace(".", "_")),
81 | # tree_depth=1))
82 | ).depend("icu").depend("Python")
83 | )
84 |
85 |
--------------------------------------------------------------------------------
/unibuild/projects/python.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2015 Sebastian Herbord. All rights reserved.
2 | #
3 | # This file is part of Mod Organizer.
4 | #
5 | # Mod Organizer is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Mod Organizer is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Mod Organizer. If not, see .
17 |
18 |
19 | from unibuild.project import Project
20 | from unibuild.modules import github, msbuild, build, urldownload
21 | from config import config
22 | from unimake import get_visual_studio_2017_or_more
23 | import os
24 | import shutil
25 | from glob import glob
26 | import errno
27 |
28 |
29 | python_version = config.get('python_version', "2.7") + config.get('python_version_minor', ".12")
30 | python_toolset = config.get('vc_platformtoolset', "v140")
31 | python_url = "https://www.python.org/ftp/python"
32 |
33 | def make_sure_path_exists(path):
34 | try:
35 | os.makedirs(path)
36 | except OSError as exception:
37 | if exception.errno != errno.EEXIST:
38 | raise
39 |
40 |
41 | def python_environment():
42 | result = config['__environment'].copy()
43 | result['Path'] += ";" + os.path.dirname(config['paths']['svn'])
44 | return result
45 |
46 |
47 | def upgrade_args():
48 | env = config['__environment']
49 | devenv_path = os.path.join(config['paths']['visual_studio_basedir'], "Common7", "IDE")
50 | #MSVC2017 supports building with the MSVC2015 toolset though this will break here, Small work around to make sure devenv.exe exists
51 | #If not try MSVC2017 instead
52 | res = os.path.isfile(os.path.join(devenv_path, "devenv.exe"))
53 | if res:
54 | return [os.path.join(devenv_path, "devenv.exe"),
55 | "PCBuild/pcbuild.sln",
56 | "/upgrade"]
57 | else:
58 | return [os.path.join(get_visual_studio_2017_or_more('15.0'),"..","..","..","Common7", "IDE", "devenv.exe"),
59 | "PCBuild/pcbuild.sln",
60 | "/upgrade"]
61 |
62 | #if config.get('prefer_binary_dependencies', False):
63 | if False:
64 | # the python installer registers in windows and prevent further installations. This means this installation
65 | # would interfere with the rest of the system
66 | filename = "python-{0}{1}.msi".format(
67 | python_version,
68 | ".amd64" if config['architecture'] == "x86_64" else ""
69 | )
70 |
71 | python = Project("Python") \
72 | .depend(build.Run("msiexec /i {0} TARGETDIR={1} /qn ADDLOCAL=DefaultFeature,SharedCRT"
73 | .format(os.path.join(config['paths']['download'], filename),
74 | os.path.join(config['paths']['build'], "python-{}".format(python_version))
75 | )
76 | )
77 | .depend(urldownload.URLDownload("{0}/{1}/{2}"
78 | .format(python_url,
79 | python_version,
80 | filename
81 | )
82 | )
83 | )
84 | )
85 | else:
86 | def install(context):
87 | make_sure_path_exists(os.path.join(config["paths"]["install"], "libs"))
88 | path_segments = [context['build_path'], "PCbuild"]
89 | if config['architecture'] == "x86_64":
90 | path_segments.append("amd64")
91 | path_segments.append("*.lib")
92 | shutil.copy(os.path.join(python['build_path'],"PC", "pyconfig.h"),os.path.join(python['build_path'], "Include","pyconfig.h"))
93 | for f in glob(os.path.join(*path_segments)):
94 | shutil.copy(f, os.path.join(config["paths"]["install"], "libs"))
95 | return True
96 |
97 | python = Project("Python") \
98 | .depend(build.Execute(install)
99 | .depend(msbuild.MSBuild("PCBuild/PCBuild.sln", "python,pyexpat",
100 | project_PlatformToolset=python_toolset)
101 | # .depend(build.Run(r'PCBuild\\build.bat -e -c Release -m -p {} "/p:PlatformToolset={}"'.format("x64" if config['architecture'] == 'x86_64' else "x86",config['vc_platform']),
102 | # environment=python_environment(),
103 | # working_directory=lambda: os.path.join(python['build_path']))
104 | .depend(build.Run(upgrade_args, name="upgrade python project")
105 | .depend(github.Source("LePresidente", "cpython", config.get('python_version', "2.7"))\
106 | .set_destination("python-{}".format(python_version))))
107 | )
108 | )
109 |
--------------------------------------------------------------------------------
/unibuild/projects/icu.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2015 Sebastian Herbord. All rights reserved.
2 | #
3 | # This file is part of Mod Organizer.
4 | #
5 | # Mod Organizer is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Mod Organizer is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Mod Organizer. If not, see .
17 |
18 | from unibuild import Project
19 | from unibuild.modules import build, sourceforge, Patch
20 | from config import config
21 | import subprocess
22 | import os
23 | import logging
24 |
25 | icu_version = config['icu_version']
26 | icu_version_minor = config['icu_version_minor']
27 |
28 | # installation happens concurrently in separate process. We need to wait for all relevant files to exist,
29 | # and can determine failure only by timeout
30 | timeout = 15 # seconds
31 |
32 |
33 | def icu_environment():
34 | s = ""
35 | MSVCFolders = ("Microsoft Visual Studio","MSBuild","Framework","Windows Kits","Microsoft SDK", "HTML Help")
36 | result = config['__environment'].copy()
37 | a = result['PATH']
38 | for x in a.split(";"):
39 | if any(word in x for word in MSVCFolders):
40 | if s:
41 | s += x + ";"
42 | else:
43 | s = x + ";"
44 | else:
45 | if "cygwin" not in x:
46 | s +=os.path.join(config['paths']['build'], "cygwin", "bin") + ";" + x + ";"
47 | else:
48 | s += x + ";"
49 | result['PATH'] = s
50 | return result
51 |
52 |
53 | # Warning, build_run only works for me if cygwin is first after VS in the path (as requested in readme)
54 | # So I change my path before calling unimake.py
55 | build_icu = build.Run("make && make install".format(os.path.join(config['paths']['build'], "cygwin", "bin")),
56 | name="ICU Make",
57 | environment=icu_environment(),
58 | working_directory=lambda: os.path.join(config["paths"]["build"], "icu", "source"))
59 |
60 |
61 | # Warning this won't work if there are Embarcadero compiler definition in your path
62 | class ConfigureIcu(build.Builder):
63 | def __init__(self):
64 | super(ConfigureIcu, self).__init__()
65 |
66 | @property
67 | def name(self):
68 | return "icu configure"
69 |
70 | def process(self, progress):
71 | from distutils.spawn import find_executable
72 | res = find_executable("cygpath", os.path.join(config['paths']['build'], "cygwin", "bin"))
73 | if res is not None:
74 | current_dir_cygwin = subprocess.check_output("{0} {1}"
75 | .format(res,
76 | os.path.join(config["paths"]["build"], "icu", "dist")))
77 |
78 | soutpath = os.path.join(self._context["build_path"], "stdout.log")
79 | serrpath = os.path.join(self._context["build_path"], "stderr.log")
80 | with open(soutpath, "w") as sout:
81 | with open(serrpath, "w") as serr:
82 | res = find_executable("bash", os.path.join(config['paths']['build'], "cygwin", "bin"))
83 | proc = subprocess.Popen([res, "runConfigureICU", "Cygwin/MSVC", "--prefix"
84 | , "{}".format(current_dir_cygwin)],
85 | env=icu_environment(),
86 | cwd=os.path.join(self._context["build_path"], "source"),
87 | shell=True,
88 | stdout=sout, stderr=serr)
89 | proc.communicate()
90 | if proc.returncode != 0:
91 | logging.error("failed to run icu runConfigureICU (returncode %s), see %s and %s",
92 | proc.returncode, soutpath, serrpath)
93 | return False
94 |
95 | return True
96 |
97 |
98 | Convert_icu = build.Run(r"dos2unix -f configure",
99 | environment=icu_environment(),
100 | working_directory=lambda: os.path.join(config["paths"]["build"], "icu", "source"))
101 |
102 | icu = Project('icu') \
103 | .depend(build_icu
104 | .depend(ConfigureIcu()
105 | .depend(Convert_icu
106 | .depend(Patch.Replace("source/io/ufile.c",
107 | "#if U_PLATFORM_USES_ONLY_WIN32_API",
108 | "#if U_PLATFORM_USES_ONLY_WIN32_API && _MSC_VER < 1900")
109 | .depend(sourceforge.Release("icu","ICU4C/{0}.{1}/icu4c-{0}_{1}-src.tgz"
110 | .format(icu_version,icu_version_minor),tree_depth=1)
111 | .set_destination("icu")
112 | )
113 | )
114 | )
115 | )
116 | )\
117 | .depend("cygwin")
118 |
--------------------------------------------------------------------------------
/unibuild/modules/git.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2015 Sebastian Herbord. All rights reserved.
2 | #
3 | # This file is part of Mod Organizer.
4 | #
5 | # Mod Organizer is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Mod Organizer is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Mod Organizer. If not, see .
17 |
18 |
19 | from subprocess import Popen
20 | from config import config
21 | from repository import Repository
22 | import os
23 | import logging
24 | from unibuild import Task
25 | from urlparse import urlparse, urlsplit
26 |
27 |
28 | class SuperRepository(Task):
29 | def __init__(self, name):
30 | super(SuperRepository, self).__init__()
31 | self.__name = name
32 | self.__context_data = {}
33 | self.prepare()
34 |
35 | def prepare(self):
36 | self.__context_data['build_path'] = os.path.join(config["paths"]["build"], self.__name)
37 |
38 | @property
39 | def path(self):
40 | return self.__context_data['build_path']
41 |
42 | @property
43 | def name(self):
44 | return self.__name
45 |
46 | def __getitem__(self, key):
47 | return self.__context_data[key]
48 |
49 | def __setitem__(self, key, value):
50 | self.__context_data[key] = value
51 |
52 | def __contains__(self, keys):
53 | return self.__context_data.__contains__(keys)
54 |
55 | def process(self, progress):
56 | if not os.path.isdir(self.path):
57 | os.makedirs(self.path)
58 | proc = Popen([config['paths']['git'], "init"],
59 | cwd=self.path,
60 | env=config['__environment'])
61 | proc.communicate()
62 | if proc.returncode != 0:
63 | logging.error("failed to init superproject %s (returncode %s)", self._name, proc.returncode)
64 | return False
65 |
66 | return True
67 |
68 |
69 | class Clone(Repository):
70 | def __init__(self, url, branch, super_repository=None, update=True, commit=None):
71 | super(Clone, self).__init__(url, branch)
72 |
73 | self.__super_repository = super_repository
74 | self.__base_name = os.path.basename(self._url)
75 | self.__update = update
76 | self.__commit = commit
77 | if self.__super_repository is not None:
78 | self._output_file_path = os.path.join(self.__super_repository.path, self.__determine_name())
79 | self.depend(super_repository)
80 |
81 | def __determine_name(self):
82 | return self.__base_name
83 |
84 | def prepare(self):
85 | self._context['build_path'] = self._output_file_path
86 |
87 | def process(self, progress):
88 | proc = None
89 | if os.path.exists(os.path.join(self._output_file_path, ".git")):
90 | if self.__update and not config.get('offline', False):
91 | proc = Popen([config['paths']['git'], "pull", self._url, self._branch],
92 | cwd=self._output_file_path,
93 | env=config["__environment"])
94 | else:
95 | if self.__super_repository is not None:
96 | proc = Popen([config['paths']['git'], "submodule", "add", "-b", self._branch,
97 | "--force", "--name", self.__base_name,
98 | self._url, self.__base_name
99 | ],
100 | cwd=self.__super_repository.path,
101 | env=config['__environment'])
102 | else:
103 | proc = Popen([config['paths']['git'], "clone", "-b", self._branch,
104 | self._url, self._context["build_path"]],
105 | env=config["__environment"])
106 |
107 | if proc is not None:
108 | proc.communicate()
109 | if proc.returncode != 0:
110 | logging.error("failed to clone repository %s (returncode %s)", self._url, proc.returncode)
111 | return False
112 |
113 | if self.__commit is not None:
114 | proc = Popen([config['paths']['git'], "checkout", self.__commit],
115 | cwd = self._context["build_path"],
116 | env=config["__environment"])
117 |
118 | if proc is not None:
119 | proc.communicate()
120 | if proc.returncode != 0:
121 | logging.error("failed to checkout repository %s (returncode %s)", self._url, proc.returncode)
122 | return False
123 |
124 | return True
125 |
126 | @staticmethod
127 | def _expiration():
128 | return config.get('repo_update_frequency', 60 * 60 * 24) # default: one day
129 |
130 | def set_destination(self, destination_name):
131 | self.__base_name = destination_name.replace("/", os.path.sep)
132 | if self.__super_repository is not None:
133 | self._output_file_path = os.path.join(self.__super_repository.path, self.__base_name)
134 | else:
135 | self._output_file_path = os.path.join(config["paths"]["build"], self.__base_name)
136 | return self
137 |
--------------------------------------------------------------------------------
/unibuild/projects/pyqt5.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2015 Sebastian Herbord. All rights reserved.
2 | #
3 | # This file is part of Mod Organizer.
4 | #
5 | # Mod Organizer is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Mod Organizer is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Mod Organizer. If not, see .
17 |
18 |
19 | from unibuild.modules import sourceforge, build, Patch
20 | from unibuild.utility import lazy
21 | from unibuild.utility.lazy import doclambda
22 | from unibuild import Project
23 | from config import config
24 | from subprocess import Popen
25 | from glob import glob
26 | import errno
27 | import shutil
28 | import os
29 | import logging
30 |
31 | import qt5 # import to get at qt version information
32 | import sip
33 | import python
34 |
35 | icu_version = config['icu_version']
36 |
37 | def make_sure_path_exists(path):
38 | try:
39 | os.makedirs(path)
40 | except OSError as exception:
41 | if exception.errno != errno.EEXIST:
42 | raise
43 |
44 |
45 | def pyqt5_env():
46 | res = config['__environment'].copy()
47 | res['path'] = ";".join([
48 | os.path.join(config['paths']['build'], "qt5", "bin"),
49 | os.path.join(config['paths']['build'], "sip-{}".format(sip.sip_version), "sipgen"),
50 | ]) + ";" + res['path']
51 | res['LIB'] = os.path.join(config["__build_base_path"], "install", "libs") + ";" + res['LIB']
52 | res['pythonhome'] = python.python['build_path']
53 | return res
54 |
55 |
56 | def copy_pyd(context):
57 | make_sure_path_exists(os.path.join(config["__build_base_path"], "install", "bin", "plugins", "data", "PyQt5"))
58 | srcdir = os.path.join(python.python['build_path'], "Lib", "site-packages", "PyQt5")
59 | dstdir = os.path.join(config["__build_base_path"], "install", "bin", "plugins", "data", "PyQt5")
60 | shutil.copy(os.path.join(srcdir, "__init__.py"),dstdir)
61 | shutil.copy(os.path.join(srcdir, "QtCore.pyd"), dstdir)
62 | shutil.copy(os.path.join(srcdir, "QtGui.pyd"),dstdir)
63 | shutil.copy(os.path.join(srcdir, "QtWidgets.pyd"), dstdir)
64 | return True
65 |
66 |
67 | class PyQt5Configure(build.Builder):
68 | def __init__(self):
69 | super(PyQt5Configure, self).__init__()
70 |
71 | @property
72 | def name(self):
73 | return "pyqt configure"
74 |
75 | def process(self, progress):
76 | soutpath = os.path.join(self._context["build_path"], "stdout.log")
77 | serrpath = os.path.join(self._context["build_path"], "stderr.log")
78 | with open(soutpath, "w") as sout:
79 | with open(serrpath, "w") as serr:
80 | bp = python.python['build_path']
81 |
82 | proc = Popen([os.path.join(python.python['build_path'],"PCbuild","amd64","python.exe"), "configure.py", "--confirm-license",
83 | "-b", bp,
84 | "-d", os.path.join(bp, "Lib", "site-packages"),
85 | "-v", os.path.join(bp, "sip", "PyQt5"),
86 | "--sip-incdir", os.path.join(bp, "Include"),
87 | "--spec=win32-msvc"],
88 | env=pyqt5_env(),
89 | cwd=self._context["build_path"],
90 | shell=True,
91 | stdout=sout, stderr=serr)
92 | proc.communicate()
93 | if proc.returncode != 0:
94 | logging.error("failed to run pyqt configure.py (returncode %s), see %s and %s",
95 | proc.returncode, soutpath, serrpath)
96 | return False
97 |
98 | return True
99 |
100 |
101 | Project("PyQt5") \
102 | .depend(build.Execute(copy_pyd)
103 | .depend(Patch.Copy([os.path.join(qt5.qt_inst_path, "bin", "Qt5Core.dll"),
104 | os.path.join(qt5.qt_inst_path, "bin", "Qt5Xml.dll"),
105 | os.path.join(config['paths']['build'], "icu" , "dist", "lib", "icudt{}.dll".format(icu_version)),
106 | os.path.join(config['paths']['build'], "icu", "dist", "lib", "icuin{}.dll".format(icu_version)),
107 | os.path.join(config['paths']['build'], "icu", "dist", "lib", "icuuc{}.dll".format(icu_version))],
108 | doclambda(lambda: python.python['build_path'], "python path"))
109 | .depend(build.Make(environment=lazy.Evaluate(pyqt5_env)).install()
110 | .depend(PyQt5Configure()
111 | .depend("sip")
112 | .depend("Qt5")
113 | .depend(sourceforge.Release("pyqt",
114 | "PyQt5/PyQt-{0}.{1}/PyQt5_gpl-{0}.{1}.zip"
115 | .format(qt5.qt_version, qt5.qt_version_minor),
116 | tree_depth=1))
117 | )
118 | )
119 | )
120 | )
121 |
122 |
--------------------------------------------------------------------------------
/config.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2015 Sebastian Herbord. All rights reserved.
2 | #
3 | # This file is part of Mod Organizer.
4 | #
5 | # Mod Organizer is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Mod Organizer is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Mod Organizer. If not, see .
17 |
18 |
19 | from _winreg import *
20 | from unibuild.utility.lazy import Lazy
21 | import os
22 | import multiprocessing
23 |
24 | global missing_prerequisites
25 | missing_prerequisites = False
26 |
27 |
28 | def path_or_default(filename, *default):
29 | from distutils.spawn import find_executable
30 | defaults = gen_search_folders(*default)
31 | res = find_executable(filename, os.environ['PATH'] + ";" + ";".join(defaults))
32 | if res is None:
33 | print 'Cannot find', filename, 'on your path or in', os.path.join('', *default)
34 | global missing_prerequisites
35 | missing_prerequisites = True
36 | return res
37 |
38 |
39 | def get_from_hklm(path, name, wow64=False):
40 | flags = KEY_READ
41 | if wow64:
42 | flags |= KEY_WOW64_32KEY
43 |
44 | # avoids crashing if a product is not present
45 | try:
46 | with OpenKey(HKEY_LOCAL_MACHINE, path, 0, flags) as key:
47 | return QueryValueEx(key, name)[0]
48 | except:
49 | return ""
50 |
51 | # To detect the editon of VS installed as of VS 2017
52 | vs_editions = [
53 | "enterprise",
54 | "professional",
55 | "community",
56 | ]
57 |
58 |
59 | program_files_folders = [
60 | os.environ['ProgramFiles(x86)'],
61 | os.environ['ProgramFiles'],
62 | os.environ['ProgramW6432'],
63 | "C:\\",
64 | "D:\\"
65 | ]
66 |
67 |
68 | def gen_search_folders(*subpath):
69 | return [
70 | os.path.join(search_folder, *subpath)
71 | for search_folder in program_files_folders
72 | ]
73 |
74 | config = {
75 | 'tools': {
76 | 'make': "nmake",
77 | },
78 | 'architecture': 'x86_64', # Don't change this as we spawn the usvfs x86 build later on.
79 | 'vc_version': '14.0',
80 | 'vc_platformtoolset': 'v140',
81 | 'vc_CustomInstallPath': '', # If you installed VC to a custom location put the full path here
82 | # eg. E:\Microsoft Visual Studio 14.0
83 | 'build_type': "RelWithDebInfo",
84 | 'offline': False, # if set, non-mandatory network requests won't be made.
85 | # This is stuff like updating source repositories. The initial
86 | # download of course can't be surpressed.
87 | 'prefer_binary_dependencies': False, # currently non-functional
88 | 'optimize': False, # activate link-time code generation and other optimization.
89 | # This massively increases build time but produces smaller
90 | # binaries and marginally faster code
91 | 'Installer': True, # Used to create installer at end of build, Forces everything to be built
92 | 'repo_update_frequency': 60 * 60 * 24, # in seconds
93 | 'num_jobs': multiprocessing.cpu_count() + 1,
94 |
95 | 'Main_Author': 'LePresidente', # the current maintainer
96 | 'Distrib_Author': 'TanninOne', # the current distribution (and the original Author)
97 | 'Work_Author': 'Hugues92', # yourself
98 |
99 | 'qt_version': '5.8', # currently evolving
100 | 'openssl_version': '1.0.2k', # changes often, so better to edit here
101 | 'zlib_version': '1.2.11', # changes often, so better to edit here
102 | 'grep_version': '2.5.4', # moved here as commented in qt5.py
103 | 'boost_version': '1.64.0', # for -DBOOST_ROOT, also, it is either to change from here
104 | 'vc_version_for_boost': '14.0', # boost 1.63 does not support VS 2017 yet
105 | 'python_version': '2.7', # used below and in python.py
106 | 'python_version_minor': '.13', # used in python.py
107 | 'icu_version': '58', # used in PyQt5
108 | 'icu_version_minor': '2', # for consistency
109 | 'WixToolSet_Version_Binary': '311', # Wix Binary Version
110 | }
111 |
112 | config['paths'] = {
113 | 'download': "{base_dir}\\downloads",
114 | 'build': "{base_dir}\\{build_dir}",
115 | 'progress': "{base_dir}\\{progress_dir}",
116 | 'install': "{base_dir}\\{install_dir}",
117 | # 'graphviz': path_or_default("dot.exe", "Graphviz2.38", "bin"),
118 | 'cmake': path_or_default("cmake.exe", "CMake", "bin"),
119 | 'git': path_or_default("git.exe", "Git", "bin"),
120 | 'perl': path_or_default("perl.exe", "StrawberryPerl", "bin"),
121 | 'ruby': path_or_default("ruby.exe", "Ruby22-x64", "bin"),
122 | 'svn': path_or_default("svn.exe", "SlikSvn", "bin"),
123 | '7z': path_or_default("7z.exe", "7-Zip"),
124 | # we need a python that matches the build architecture
125 | 'python': Lazy(lambda: os.path.join(get_from_hklm(r"SOFTWARE\Python\PythonCore\{}\InstallPath".format(config['python_version']),
126 | "", config['architecture'] == "x86"),
127 | "python.exe")),
128 | 'visual_studio_base': "",
129 | 'visual_studio': "" # will be set in unimake.py after args are evaluated
130 | }
131 |
132 | if missing_prerequisites:
133 | print '\nMissing prerequisites listed above - cannot continue'
134 | exit(1)
135 |
--------------------------------------------------------------------------------
/libpatterns.py:
--------------------------------------------------------------------------------
1 | patterns = [
2 | "Lib/BaseHTTPServer.py",
3 | "Lib/Bastion.py",
4 | "Lib/CGIHTTPServer.py",
5 | "Lib/ConfigParser.py",
6 | "Lib/ConfigParser.pyo",
7 | "Lib/Cookie.py",
8 | "Lib/DocXMLRPCServer.py",
9 | "Lib/HTMLParser.py",
10 | "Lib/MimeWriter.py",
11 | "Lib/Queue.py",
12 | "Lib/SimpleHTTPServer.py",
13 | "Lib/SimpleXM",
14 | "Lib/SocketServer.py",
15 | "Lib/StringIO.py",
16 | "Lib/UserList.py",
17 | "Lib/UserDict.py",
18 | "Lib/UserDict.pyo",
19 | "Lib/UserString.py",
20 | "Lib/_LWPCookieJar.py",
21 | "Lib/_MozillaCookieJar.py",
22 | "Lib/__future__.py",
23 | "Lib/_abcoll.py",
24 | "Lib/_abcoll.pyo",
25 | "Lib/_pyio.py",
26 | "Lib/_strptime.py",
27 | "Lib/_threading_local.py",
28 | "Lib/_weakrefset.py",
29 | "Lib/_weakrefset.pyo",
30 | "Lib/abc.py",
31 | "Lib/abc.pyo",
32 | "Lib/aifc.py",
33 | "Lib/antigravity.py",
34 | "Lib/anydbm.py",
35 | "Lib/argparse.py",
36 | "Lib/ast.py",
37 | "Lib/asynchat.py",
38 | "Lib/asyncore.py",
39 | "Lib/atexit.py",
40 | "Lib/atexit.pyo",
41 | "Lib/audiodev.py",
42 | "Lib/base64.py",
43 | "Lib/bdb.py",
44 | "Lib/binhex.py",
45 | "Lib/bisect.py",
46 | "Lib/cProfile.py",
47 | "Lib/calendar.py",
48 | "Lib/cgi.py",
49 | "Lib/cgitb.py",
50 | "Lib/chunk.py",
51 | "Lib/cmd.py",
52 | "Lib/code.py",
53 | "Lib/codecs.py",
54 | "Lib/codecs.pyo",
55 | "Lib/codeop.py",
56 | "Lib/collections.py",
57 | "Lib/collections.pyo",
58 | "Lib/colorsys.py",
59 | "Lib/commands.py",
60 | "Lib/compileall.py",
61 | "Lib/contextlib.py",
62 | "Lib/cookielib.py",
63 | "Lib/copy.py",
64 | "Lib/copy_reg.py",
65 | "Lib/copy_reg.pyo",
66 | "Lib/csv.py",
67 | "Lib/dbhash.py",
68 | "Lib/decimal.py",
69 | "Lib/difflib.py",
70 | "Lib/dircache.py",
71 | "Lib/dis.py",
72 | "Lib/doctest.py",
73 | "Lib/dumbdbm.py",
74 | "Lib/dummy_thread.py",
75 | "Lib/dummy_threading.py",
76 | "Lib/filecmp.py",
77 | "Lib/fileinput.py",
78 | "Lib/fnmatch.py",
79 | "Lib/formatter.py",
80 | "Lib/fpformat.py",
81 | "Lib/fractions.py",
82 | "Lib/ftplib.py",
83 | "Lib/functools.py",
84 | "Lib/functools.pyo",
85 | "Lib/genericpath.py",
86 | "Lib/genericpath.pyo",
87 | "Lib/getopt.py",
88 | "Lib/getpass.py",
89 | "Lib/gettext.py",
90 | "Lib/glob.py",
91 | "Lib/gzip.py",
92 | "Lib/hashlib.py",
93 | "Lib/heapq.py",
94 | "Lib/heapq.pyo",
95 | "Lib/hmac.py",
96 | "Lib/htmlentitydefs.py",
97 | "Lib/htmllib.py",
98 | "Lib/httplib.py",
99 | "Lib/ihooks.py",
100 | "Lib/imaplib.py",
101 | "Lib/imghdr.py",
102 | "Lib/imputil.py",
103 | "Lib/inspect.py",
104 | "Lib/io.py",
105 | "Lib/keyword.py",
106 | "Lib/keyword.pyo",
107 | "Lib/linecache.py",
108 | "Lib/linecache.pyo",
109 | "Lib/locale.py",
110 | "Lib/locale.pyo",
111 | "Lib/macpath.py",
112 | "Lib/macurl2path.py",
113 | "Lib/mailbox.py",
114 | "Lib/mailcap.py",
115 | "Lib/markupbase.py",
116 | "Lib/md5.py",
117 | "Lib/mhlib.py",
118 | "Lib/mimetools.py",
119 | "Lib/mimetypes.py",
120 | "Lib/mimify.py",
121 | "Lib/modulefinder.py",
122 | "Lib/multifile.py",
123 | "Lib/mutex.py",
124 | "Lib/netrc.py",
125 | "Lib/new.py",
126 | "Lib/nntplib.py",
127 | "Lib/ntpath.py",
128 | "Lib/ntpath.pyo",
129 | "Lib/nturl2path.py",
130 | "Lib/numbers.py",
131 | "Lib/opcode.py",
132 | "Lib/optparse.py",
133 | "Lib/os.py",
134 | "Lib/os.pyo",
135 | "Lib/os2emxpath.py",
136 | "Lib/pdb.py",
137 | "Lib/pickle.py",
138 | "Lib/pickletools.py",
139 | "Lib/pipes.py",
140 | "Lib/pkgutil.py",
141 | "Lib/platform.py",
142 | "Lib/plistlib.py",
143 | "Lib/popen2.py",
144 | "Lib/poplib.py",
145 | "Lib/posixfile.py",
146 | "Lib/posixpath.py",
147 | "Lib/pprint.py",
148 | "Lib/profile.py",
149 | "Lib/pstats.py",
150 | "Lib/pty.py",
151 | "Lib/py_compile.py",
152 | "Lib/pyclbr.py",
153 | "Lib/quopri.py",
154 | "Lib/random.py",
155 | "Lib/re.py",
156 | "Lib/re.pyo",
157 | "Lib/repr.py",
158 | "Lib/rexec.py",
159 | "Lib/rfc822.py",
160 | "Lib/rlcompleter.py",
161 | "Lib/robotparser.py",
162 | "Lib/runpy.py",
163 | "Lib/sched.py",
164 | "Lib/sets.py",
165 | "Lib/sgmllib.py",
166 | "Lib/sha.py",
167 | "Lib/shelve.py",
168 | "Lib/shlex.py",
169 | "Lib/shutil.py",
170 | "Lib/sip.pyd",
171 | "Lib/site.py",
172 | "Lib/site.pyo",
173 | "Lib/smtpd.py",
174 | "Lib/smtplib.py",
175 | "Lib/sndhdr.py",
176 | "Lib/socket.py",
177 | "Lib/sre.py",
178 | "Lib/sre_compile.py",
179 | "Lib/sre_compile.pyo",
180 | "Lib/sre_constants.py",
181 | "Lib/sre_constants.pyo",
182 | "Lib/sre_parse.py",
183 | "Lib/sre_parse.pyo",
184 | "Lib/ssl.py",
185 | "Lib/stat.py",
186 | "Lib/stat.pyo",
187 | "Lib/statvfs.py",
188 | "Lib/string.py",
189 | "Lib/stringold.py",
190 | "Lib/stringprep.py",
191 | "Lib/struct.py",
192 | "Lib/struct.pyo",
193 | "Lib/subprocess.py",
194 | "Lib/sunau.py",
195 | "Lib/sunaudio.py",
196 | "Lib/symbol.py",
197 | "Lib/symtable.py",
198 | "Lib/sysconfig.py",
199 | "Lib/sysconfig.pyo",
200 | "Lib/tabnanny.py",
201 | "Lib/tarfile.py",
202 | "Lib/telnetlib.py",
203 | "Lib/tempfile.py",
204 | "Lib/textwrap.py",
205 | "Lib/this.py",
206 | "Lib/threading.py",
207 | "Lib/timeit.py",
208 | "Lib/toaiff.py",
209 | "Lib/token.py",
210 | "Lib/tokenize.py",
211 | "Lib/trace.py",
212 | "Lib/traceback.py",
213 | "Lib/traceback.pyo",
214 | "Lib/tty.py",
215 | "Lib/types.py",
216 | "Lib/types.pyo",
217 | "Lib/urllib.py",
218 | "Lib/urllib2.py",
219 | "Lib/urlparse.py",
220 | "Lib/user.py",
221 | "Lib/uu.py",
222 | "Lib/uuid.py",
223 | "Lib/warnings.py",
224 | "Lib/warnings.pyo",
225 | "Lib/wave.py",
226 | "Lib/weakref.py",
227 | "Lib/webbrowser.py",
228 | "Lib/whichdb.py",
229 | "Lib/xdrlib.py",
230 | "Lib/xmllib.py",
231 | "Lib/xmlrpclib.py",
232 | "Lib/zipfile.py",
233 |
234 | "Lib/ctypes/*",
235 | "Lib/ctypes/macholib/*",
236 | "Lib/email/*",
237 | "Lib/email/mime/*",
238 | "Lib/encodings/*",
239 | "Lib/importlib/*",
240 | "Lib/json/*",
241 | "Lib/logging/*",
242 | "Lib/multiprocessing/*",
243 | "Lib/multiprocessing/dummy/*",
244 | "Lib/sqlite3/*",
245 | "Lib/wsgiref/*",
246 | "Lib/xml/__init__.py",
247 | "Lib/xml/dom/*",
248 | "Lib/xml/etree/*",
249 | "Lib/xml/parsers/*",
250 | "Lib/xml/sax/*",
251 | ]
252 |
--------------------------------------------------------------------------------
/unibuild/modules/urldownload.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2015 Sebastian Herbord. All rights reserved.
2 | #
3 | # This file is part of Mod Organizer.
4 | #
5 | # Mod Organizer is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Mod Organizer is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Mod Organizer. If not, see .
17 |
18 |
19 | from unibuild.retrieval import Retrieval
20 | from config import config
21 | import os
22 | import sys
23 | import logging
24 | from urlparse import urlparse
25 | import urllib2
26 | import tarfile
27 | import zipfile
28 | import subprocess
29 | import shutil
30 | from unibuild.utility import ProgressFile
31 | from unibuild.utility.context_objects import on_failure
32 |
33 |
34 | class URLDownload(Retrieval):
35 |
36 | BLOCK_SIZE = 8192
37 |
38 | def __init__(self, url, tree_depth=0):
39 | super(URLDownload, self).__init__()
40 | self.__url = url
41 | self.__tree_depth = tree_depth
42 | self.__file_name = os.path.basename(urlparse(self.__url).path)
43 |
44 | @property
45 | def name(self):
46 | return "download {0}".format(self.__file_name)
47 |
48 | def set_destination(self, destination_name):
49 | name, ext = os.path.splitext(self.__file_name)
50 | if name.lower().endswith(".tar"):
51 | ext = ".tar" + ext
52 |
53 | self.__file_name = destination_name + ext
54 | return self
55 |
56 | def prepare(self):
57 | name, ext = os.path.splitext(self.__file_name)
58 | if name.lower().endswith(".tar"):
59 | name, e2 = os.path.splitext(name)
60 |
61 | if 'build_path' not in self._context:
62 | output_file_path = os.path.join(config['paths']['build'], name)
63 | self._context['build_path'] = output_file_path
64 |
65 | def process(self, progress):
66 | logging.info("processing download")
67 | output_file_path = self._context['build_path']
68 | archive_file_path = os.path.join(config['paths']['download'], self.__file_name)
69 |
70 | if os.path.isfile(output_file_path):
71 | logging.info("File already extracted: {0}".format(archive_file_path))
72 | else:
73 | if os.path.isfile(archive_file_path):
74 | logging.info("File already downloaded: {0}".format(archive_file_path))
75 | else:
76 | logging.info("File not yet downloaded: {0}".format(archive_file_path))
77 | self.download(archive_file_path, progress)
78 | progress.finish()
79 |
80 | if not self.extract(archive_file_path, output_file_path, progress):
81 | return False
82 | progress.finish()
83 |
84 | builddir = os.listdir(self._context["build_path"])
85 | if len(builddir) == 1:
86 | self._context["build_path"] = os.path.join(self._context["build_path"], builddir[0])
87 | return True
88 |
89 | def download(self, output_file_path, progress):
90 | logging.info("Downloading {} to {}".format(self.__url, output_file_path))
91 | progress.job = "Downloading"
92 | data = urllib2.urlopen(self.__url)
93 | with open(output_file_path, 'wb') as outfile:
94 | meta = data.info()
95 | length_str = meta.getheaders("Content-Length")
96 | if length_str:
97 | progress.maximum = int(length_str[0])
98 | else:
99 | progress.maximum = sys.maxint
100 |
101 | bytes_read = 0
102 | while True:
103 | block = data.read(URLDownload.BLOCK_SIZE)
104 | if not block:
105 | break
106 | bytes_read += len(block)
107 | outfile.write(block)
108 | progress.value = bytes_read
109 |
110 | def extract(self, archive_file_path, output_file_path, progress):
111 | def progress_func(pos, size):
112 | progress.value = int(pos * 100 / size)
113 |
114 | logging.info("Extracting {0}".format(self.__url))
115 |
116 | progress.value = 0
117 | progress.job = "Extracting"
118 | output_file_path = u"\\\\?\\" + os.path.abspath(output_file_path)
119 |
120 | try:
121 | os.makedirs(output_file_path)
122 | except Exception:
123 | # doesn't matter if the directory already exists.
124 | pass
125 |
126 | with on_failure(lambda: shutil.rmtree(output_file_path)):
127 | filename, extension = os.path.splitext(self.__file_name)
128 | if extension == ".gz" or extension == ".tgz":
129 | archive_file = ProgressFile(archive_file_path, progress_func)
130 | with tarfile.open(fileobj=archive_file, mode='r:gz') as arch:
131 | arch.extractall(output_file_path)
132 | archive_file.close()
133 | elif extension == ".bz2":
134 | archive_file = ProgressFile(archive_file_path, progress_func)
135 | with tarfile.open(fileobj=archive_file, mode='r:bz2') as arch:
136 | arch.extractall(output_file_path)
137 | archive_file.close()
138 | elif extension == ".zip":
139 | archive_file = ProgressFile(archive_file_path, progress_func)
140 | with zipfile.ZipFile(archive_file) as arch:
141 | arch.extractall(output_file_path)
142 | archive_file.close()
143 | elif extension == ".7z":
144 | proc = subprocess.Popen([config['paths']['7z'], "x", archive_file_path, "-o{}".format(output_file_path)])
145 | if proc.wait() != 0:
146 | return False
147 | elif extension in [".exe", ".msi"]:
148 | # installers need to be handled by the caller
149 | return True
150 | else:
151 | logging.error("unsupported file extension {0}".format(extension))
152 | return False
153 |
154 | for i in range(self.__tree_depth):
155 | sub_dirs = os.listdir(output_file_path)
156 | if len(sub_dirs) != 1:
157 | raise ValueError("unexpected archive structure,"
158 | " expected exactly one directory in {}".format(output_file_path))
159 | source_dir = os.path.join(output_file_path, sub_dirs[0])
160 |
161 | for src in os.listdir(source_dir):
162 | shutil.move(os.path.join(source_dir, src), output_file_path)
163 |
164 | shutil.rmtree(source_dir)
165 | return True
166 |
--------------------------------------------------------------------------------
/unibuild/modules/cmake.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2015 Sebastian Herbord. All rights reserved.
2 | #
3 | # This file is part of Mod Organizer.
4 | #
5 | # Mod Organizer is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Mod Organizer is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Mod Organizer. If not, see .
17 |
18 |
19 | from unibuild.builder import Builder
20 | from unibuild.utility.enum import enum
21 | from unibuild.utility.context_objects import on_exit
22 | from subprocess import Popen, PIPE
23 | from config import config
24 | import os.path
25 | import logging
26 | import shutil
27 | import re
28 |
29 |
30 | class CMake(Builder):
31 |
32 | def __init__(self):
33 | super(CMake, self).__init__()
34 | self.__arguments = []
35 | self.__install = False
36 |
37 | @property
38 | def name(self):
39 | if self._context is None:
40 | return "cmake"
41 | else:
42 | return "cmake {0}".format(self._context.name)
43 |
44 | def applies(self, parameters):
45 | return True
46 |
47 | def fulfilled(self):
48 | return False
49 |
50 | def arguments(self, arguments):
51 | self.__arguments = arguments
52 | return self
53 |
54 | def install(self):
55 | self.__install = True
56 | return self
57 |
58 | def process(self, progress):
59 | if "build_path" not in self._context:
60 | logging.error("source path not known for {},"
61 | " are you missing a matching retrieval script?".format(self._context.name))
62 | return False
63 |
64 | # prepare for out-of-source build
65 | build_path = os.path.join(self._context["build_path"], "build")
66 | #if os.path.exists(build_path):
67 | # shutil.rmtree(build_path)
68 | try:
69 | os.mkdir(build_path)
70 | except:
71 | pass
72 |
73 | soutpath = os.path.join(self._context["build_path"], "stdout.log")
74 | serrpath = os.path.join(self._context["build_path"], "stderr.log")
75 |
76 | try:
77 | with on_exit(lambda: progress.finish()):
78 | with open(soutpath, "w") as sout:
79 | with open(serrpath, "w") as serr:
80 | proc = Popen(
81 | [config["paths"]["cmake"], "-G", "NMake Makefiles", ".."] + self.__arguments,
82 | cwd=build_path,
83 | env=config["__environment"],
84 | stdout=sout, stderr=serr)
85 | proc.communicate()
86 | if proc.returncode != 0:
87 | raise Exception("failed to generate makefile (returncode %s), see %s and %s" %
88 | (proc.returncode, soutpath, serrpath))
89 |
90 | proc = Popen([config['tools']['make'], "verbose=1"],
91 | shell=True,
92 | env=config["__environment"],
93 | cwd=build_path,
94 | stdout=PIPE, stderr=serr)
95 | progress.job = "Compiling"
96 | progress.maximum = 100
97 | while proc.poll() is None:
98 | while True:
99 | line = proc.stdout.readline()
100 | if line != '':
101 | match = re.search("^\\[([0-9 ][0-9 ][0-9])%\\]", line)
102 | if match is not None:
103 | progress.value = int(match.group(1))
104 | sout.write(line)
105 | else:
106 | break
107 |
108 | if proc.returncode != 0:
109 | raise Exception("failed to build (returncode %s), see %s and %s" %
110 | (proc.returncode, soutpath, serrpath))
111 |
112 | if self.__install:
113 | proc = Popen([config['tools']['make'], "install"],
114 | shell=True,
115 | env=config["__environment"],
116 | cwd=build_path,
117 | stdout=sout, stderr=serr)
118 | proc.communicate()
119 | if proc.returncode != 0:
120 | raise Exception("failed to install (returncode %s), see %s and %s" %
121 | (proc.returncode, soutpath, serrpath))
122 | return False
123 | except Exception, e:
124 | logging.error(e.message)
125 | return False
126 | return True
127 |
128 |
129 | class CMakeEdit(Builder):
130 |
131 | Type = enum(VC=1, CodeBlocks=2)
132 |
133 | def __init__(self, ide_type):
134 | super(CMakeEdit, self).__init__()
135 | self.__arguments = []
136 | self.__type = ide_type
137 |
138 | @property
139 | def name(self):
140 | if self._context is None:
141 | return "cmake edit"
142 | else:
143 | return "cmake edit {}".format(self._context.name)
144 |
145 | def applies(self, parameters):
146 | return True
147 |
148 | def fulfilled(self):
149 | return False
150 |
151 | def arguments(self, arguments):
152 | self.__arguments = arguments
153 | return self
154 |
155 | def __vc_year(self, version):
156 | if version == "12.0":
157 | return "2013"
158 | elif version == "14.0":
159 | return "2015"
160 |
161 | def __generator_name(self):
162 | if self.__type == CMakeEdit.Type.VC:
163 | return "Visual Studio {} {}"\
164 | .format(config['vc_version'].split('.')[0], self.__vc_year(config['vc_version']))
165 | elif self.__type == CMakeEdit.Type.CodeBlocks:
166 | return "CodeBlocks - NMake Makefiles"
167 |
168 | def prepare(self):
169 | self._context['edit_path'] = os.path.join(self._context['build_path'], "edit")
170 |
171 | def process(self, progress):
172 | if "build_path" not in self._context:
173 | logging.error("source path not known for {},"
174 | " are you missing a matching retrieval script?".format(self._context.name))
175 | return False
176 |
177 | if os.path.exists(self._context['edit_path']):
178 | shutil.rmtree(self._context['edit_path'])
179 | os.mkdir(self._context['edit_path'])
180 |
181 | soutpath = os.path.join(self._context["build_path"], "stdout.log")
182 | serrpath = os.path.join(self._context["build_path"], "stderr.log")
183 |
184 | with open(soutpath, "w") as sout:
185 | with open(serrpath, "w") as serr:
186 | proc = Popen(
187 | [config["paths"]["cmake"], "-G", self.__generator_name(), ".."] + self.__arguments,
188 | cwd=self._context['edit_path'],
189 | env=config["__environment"],
190 | stdout=sout, stderr=serr)
191 | proc.communicate()
192 | if proc.returncode != 0:
193 | logging.error("failed to generate makefile (returncode %s), see %s and %s",
194 | proc.returncode, soutpath, serrpath)
195 | return False
196 |
197 | return True
198 |
--------------------------------------------------------------------------------
/unibuild/projects/qt5.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2015 Sebastian Herbord. All rights reserved.
2 | #
3 | # This file is part of Mod Organizer.
4 | #
5 | # Mod Organizer is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Mod Organizer is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Mod Organizer. If not, see .
17 |
18 |
19 | from unibuild import Project, Task
20 | from unibuild.modules import build, Patch, git, urldownload, sourceforge, dummy
21 | from config import config
22 | import os
23 | import itertools
24 | from glob import glob
25 | import shutil
26 | import python
27 | import errno
28 |
29 | from unibuild.projects import openssl, cygwin, icu
30 |
31 | qt_download_url = "http://download.qt.io/official_releases/qt"
32 | qt_download_ext = "tar.gz"
33 | qt_version = config['qt_version']
34 | qt_version_minor = "1"
35 | qt_inst_path = "{}/qt5".format(config["paths"]["build"]).replace("/", os.path.sep)
36 | icu_version = config['icu_version']
37 |
38 |
39 | def bitness():
40 | return "64" if config['architecture'] == "x86_64" else "32"
41 |
42 | def variant():
43 | return "msvc2013" if config['vc_version'] == "12.0" else "msvc2015" if config['vc_version'] == "14.0" else "msvc2017"
44 |
45 | qt_bin_variant = variant()
46 |
47 | platform = "win32-{0}".format(variant())
48 |
49 | openssl_version = config['openssl_version']
50 | grep_version = config['grep_version']
51 |
52 | def make_sure_path_exists(path):
53 | try:
54 | os.makedirs(path)
55 | except OSError as exception:
56 | if exception.errno != errno.EEXIST:
57 | raise
58 |
59 |
60 | # if config.get('prefer_binary_dependencies', False):
61 |
62 | if False:
63 | # binary installation disabled because there is no support currently for headless installation
64 | filename = "qt-opensource-windows-x86-{variant}{arch}-{ver}.{ver_min}.exe".format(
65 | url=qt_download_url,
66 | ver=qt_version,
67 | ver_min=qt_version_minor,
68 | variant=qt_bin_variant,
69 | arch="_64" if config['architecture'] == 'x86_64' else ""
70 | )
71 | qt5 = Project("Qt5") \
72 | .depend(build.Run(filename, working_directory=config['paths']['download'])
73 | .depend(urldownload.URLDownload(
74 | "{url}/{ver}/{ver}.{ver_min}/{filename}"
75 | .format(url=qt_download_url,
76 | ver=qt_version,
77 | ver_min=qt_version_minor,
78 | filename=filename))))
79 | else:
80 | skip_list = ["qtactiveqt", "qtandroidextras", "qtenginio",
81 | "qtserialport", "qtsvg", "qtwebkit",
82 | "qtwayland", "qtdoc", "qtconnectivity", "qtwebkit-examples"]
83 |
84 | nomake_list = ["tests", "examples"]
85 |
86 | configure_cmd = lambda: " ".join(["configure.bat",
87 | "-platform", platform,
88 | "-debug-and-release", "-force-debug-info",
89 | "-opensource", "-confirm-license", "-icu",
90 | "-mp", "-no-compile-examples",
91 | "-no-angle", "-opengl", "desktop",
92 | "-ssl", "-openssl-linked",
93 | "OPENSSL_LIBS=\"-lssleay32MD -llibeay32MD -lgdi32 -lUser32\"",
94 | "-prefix", qt_inst_path] \
95 | + list(itertools.chain(*[("-skip", s) for s in skip_list])) \
96 | + list(itertools.chain(*[("-nomake", n) for n in nomake_list])))
97 |
98 | jom = Project("jom") \
99 | .depend(urldownload.URLDownload("http://download.qt.io/official_releases/jom/jom.zip"))
100 |
101 | def qt5_environment():
102 | result = config['__environment'].copy()
103 | result['Path'] = ";".join([
104 | os.path.join(config['paths']['build'], "icu", "dist", "bin"),
105 | os.path.join(config['paths']['build'], "icu", "dist", "lib"),
106 | os.path.join(config['paths']['build'], "jom")]) + ";" + result['Path']
107 | result['INCLUDE'] = os.path.join(config['paths']['build'], "icu", "dist", "include") + ";" + \
108 | os.path.join(config['paths']['build'], "Win{}OpenSSL-{}".format(bitness(), openssl_version.replace(".", "_")), "include") + ";" + \
109 | result['INCLUDE']
110 | result['LIB'] = os.path.join(config['paths']['build'], "icu", "dist", "lib") + ";" + \
111 | os.path.join(config['paths']['build'], "Win{}OpenSSL-{}".format(bitness(), openssl_version.replace(".", "_")), "lib", "VC") + ";" + \
112 | result['LIB']
113 | result['LIBPATH'] = os.path.join(config['paths']['build'], "icu", "dist", "lib") + ";" + result['LIBPATH']
114 | return result
115 |
116 | init_repo = build.Run("perl init-repository", name="init qt repository") \
117 | .set_fail_behaviour(Task.FailBehaviour.CONTINUE) \
118 | .depend(git.Clone("http://code.qt.io/qt/qt5.git", qt_version)) # Internet proxy could refuse git protocol
119 |
120 | build_qt5 = build.Run(r"jom.exe -j {}".format(config['num_jobs']),
121 | environment=qt5_environment(),
122 | name="Build Qt5",
123 | working_directory=lambda: os.path.join(qt5['build_path']))
124 |
125 | install_qt5 = build.Run(r"nmake install",
126 | environment=qt5_environment(),
127 | name="Install Qt5",
128 | working_directory=lambda: os.path.join(qt5['build_path']))
129 |
130 |
131 | def copy_icu_libs(context):
132 | for f in glob(os.path.join(config['paths']['build'], "icu", "dist", "lib", "icu*{}.dll".format(icu_version))):
133 | shutil.copy(f, os.path.join(config["paths"]["build"], "qt5", "bin"))
134 | return True
135 |
136 |
137 | def copy_imageformats(context):
138 | make_sure_path_exists(os.path.join(config['paths']['install'], "bin", "dlls", "imageformats"))
139 | for f in glob(os.path.join(config["paths"]["build"], "qt5.git", "qtbase", "plugins", "imageformats", "*.dll")):
140 | shutil.copy(f, os.path.join(config['paths']['install'], "bin", "dlls", "imageformats"))
141 | return True
142 |
143 |
144 | def copy_platform(context):
145 | make_sure_path_exists(os.path.join(config['paths']['install'], "bin", "platforms"))
146 | for f in glob(
147 | os.path.join(config["paths"]["build"], "qt5.git", "qtbase", "plugins", "platforms", "qwindows.dll")):
148 | shutil.copy(f, os.path.join(config['paths']['install'], "bin", "platforms"))
149 | return True
150 |
151 |
152 | qt5 = Project("Qt5") \
153 | .depend(build.Execute(copy_imageformats)
154 | .depend(build.Execute(copy_platform)
155 | .depend(build.Execute(copy_icu_libs)
156 | .depend(install_qt5
157 | .depend(build_qt5
158 | .depend("jom")
159 | .depend(build.Run(configure_cmd,
160 | name="configure qt",
161 | environment=qt5_environment())
162 | .depend(init_repo)
163 |
164 | .depend("icu")
165 | .depend("openssl")
166 | )
167 | )
168 | )
169 | )
170 | )
171 | )
172 |
173 |
174 |
--------------------------------------------------------------------------------
/unimake.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2015 Sebastian Herbord. All rights reserved.
2 | #
3 | # This file is part of Mod Organizer.
4 | #
5 | # Mod Organizer is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Mod Organizer is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Mod Organizer. If not, see .
17 |
18 |
19 | import eggs
20 | from unibuild.manager import TaskManager
21 | from unibuild.progress import Progress
22 | from unibuild.project import Project
23 | from unibuild import Task
24 | from unibuild.utility import CIDict
25 | from config import config, vs_editions, get_from_hklm
26 | from subprocess import Popen, PIPE
27 | import imp
28 | import sys
29 | import traceback
30 | import logging
31 | import networkx as nx
32 | import tempfile
33 | import os.path
34 | import argparse
35 | import re
36 |
37 | exitcode = 0
38 |
39 | def progress_callback(job, percentage):
40 | if not percentage and not job:
41 | sys.stdout.write("\n")
42 | else:
43 | pb_length = 50
44 | filled = int((pb_length * percentage) / 100) # cast to int may be necessary in python 3
45 | #sys.stdout.write("\r%d%%" % percentage)
46 | sys.stdout.write("\r%s [%s%s] %d%%" % (job, "=" * filled, " " * (pb_length - filled), percentage))
47 |
48 | sys.stdout.flush()
49 |
50 |
51 | def draw_graph(graph, filename):
52 | if config['paths']['graphviz']:
53 | # neither pydot nor pygraphviz reliably find graphviz on windows. gotta do everything myself...
54 | from subprocess import call
55 | graph_file_name = os.path.join(os.getcwd(), "graph.dot")
56 | nx.write_dot(graph, graph_file_name)
57 |
58 | call([config['paths']['graphviz'],
59 | "-Tpng", "-Edir=back", "-Gsplines=ortho", "-Grankdir=BT", "-Gconcentrate=true", "-Nshape=box", "-Gdpi=192",
60 | graph_file_name,
61 | "-o", "{}.png".format(filename)])
62 | else:
63 | print("graphviz path not set")
64 |
65 |
66 | def extract_independent(graph):
67 | """
68 | :param graph:
69 | :type graph: nx.DiGraph
70 | :return:
71 | """
72 | independent = []
73 | for node in graph.nodes_iter():
74 | if graph.out_degree(node) == 0:
75 | independent.append(node)
76 | return independent
77 |
78 |
79 | def vc_year(vc_version):
80 | return "2017" if vc_version == "15.0" else ""
81 |
82 |
83 | # No entries for vs 2017 in the stadard registry, check environment then look in the default installation dir
84 | def get_visual_studio_2017_or_more(vc_version):
85 | try:
86 | if os.environ["VisualStudioVersion"] == vc_version:
87 | p = os.path.join(os.environ["VSINSTALLDIR"], "VC", "Auxiliary", "Build")
88 | f = os.path.join(p, "vcvarsall.bat")
89 | res = os.path.isfile(f)
90 | if res is not None:
91 | return os.path.realpath(p)
92 | else:
93 | res = None
94 | except:
95 | res = None
96 |
97 | try:
98 | p = os.path.join(config['vc_CustomInstallPath'], "VC", "Auxiliary", "Build")
99 | f = os.path.join(p, "vcvarsall.bat")
100 | res = os.path.isfile(f)
101 | if res is not None:
102 | return os.path.realpath(p)
103 | else:
104 | res = None
105 | except:
106 | res = None
107 |
108 | for edition in vs_editions:
109 | s = os.environ["ProgramFiles(x86)"]
110 | p = os.path.join(s, "Microsoft Visual Studio", vc_year(vc_version), edition, "VC", "Auxiliary", "Build")
111 | f = os.path.join(p, "vcvarsall.bat")
112 | if os.path.isfile(f):
113 | config['paths']['visual_studio_basedir'] = os.path.join(s, "Microsoft Visual Studio", vc_year(vc_version), edition)
114 | return os.path.realpath(p)
115 |
116 |
117 | def get_visual_studio_2015_or_less(vc_version):
118 | res = ""
119 | try:
120 | s = os.environ["ProgramFiles(x86)"]
121 | p = os.path.join(s, "Microsoft Visual Studio {}".format(vc_version), "VC")
122 | f = os.path.join(p, "vcvarsall.bat")
123 | if os.path.isfile(f):
124 | config['paths']['visual_studio_basedir'] = os.path.join(s, "Microsoft Visual Studio {}".format(vc_version))
125 | return os.path.realpath(p)
126 | else:
127 | res = None
128 | except:
129 | res = None
130 |
131 | if res == None:
132 | try:
133 | s = os.environ["ProgramFiles(x86)"]
134 | p = os.path.join(s, "Microsoft Visual Studio", "Shared", vc_version, "VC")
135 | f = os.path.join(p, "vcvarsall.bat")
136 |
137 | if os.path.isfile(f):
138 | config['paths']['visual_studio_basedir'] = os.path.join(s, "Microsoft Visual Studio", "Shared", vc_version)
139 | return os.path.realpath(p)
140 | else:
141 | res = None
142 | except:
143 | res = None
144 |
145 | # We should try the custom VC install path as well
146 | if res == None:
147 | try:
148 | p = os.path.join(config['vc_CustomInstallPath'], "VC")
149 | f = os.path.join(p, "vcvarsall.bat")
150 | if os.path.isfile(f):
151 | config['paths']['visual_studio_basedir'] = os.path.join(config['vc_CustomInstallPath'])
152 | return os.path.realpath(p)
153 | else:
154 | res = None
155 | except:
156 | res = None
157 |
158 |
159 |
160 | def visual_studio(vc_version):
161 | config["paths"]["visual_studio"] = get_visual_studio_2015_or_less(vc_version) if vc_version < "15.0" else get_visual_studio_2017_or_more(vc_version)
162 | if not config["paths"]["visual_studio"]:
163 | logging.error("Unable to find vcvarsall.bat, please make sure you have 'Common C++ tools' Installed")
164 | return False
165 |
166 |
167 | def visual_studio_environment():
168 | # when using visual studio we need to set up the environment correctly
169 | arch = "amd64" if config["architecture"] == 'x86_64' else "x86"
170 | if config['paths']['visual_studio']:
171 | proc = Popen([os.path.join(config['paths']['visual_studio'], "vcvarsall.bat"), arch, "&&", "SET"],
172 | stdout=PIPE, stderr=PIPE)
173 | stdout, stderr = proc.communicate()
174 |
175 | if "Error in script usage. The correct usage is" in stderr:
176 | logging.error("failed to set up environment (returncode %s): %s", proc.returncode, stderr)
177 | return False
178 |
179 | if "Error in script usage. The correct usage is" in stdout:
180 | logging.error("failed to set up environment (returncode %s): %s", proc.returncode, stderr)
181 | return False
182 |
183 | if proc.returncode != 0:
184 | logging.error("failed to set up environment (returncode %s): %s", proc.returncode, stderr)
185 | return False
186 | else:
187 | sys.exit(1)
188 |
189 |
190 | vcenv = CIDict()
191 |
192 | for line in stdout.splitlines():
193 | if "=" in line:
194 | key, value = line.split("=", 1)
195 | vcenv[key] = value
196 | return vcenv
197 |
198 |
199 | def init_config(args):
200 | for d in config['paths'].keys():
201 | if isinstance(config['paths'][d], str):
202 | config['paths'][d] = config['paths'][d].format(base_dir=os.path.abspath(args.destination),
203 | build_dir=args.builddir,
204 | progress_dir=args.progressdir,
205 | install_dir=args.installdir)
206 |
207 |
208 | if args.set:
209 | for setting in args.set:
210 | key, value = setting.split('=', 2)
211 | path = key.split('.')
212 | cur = config
213 | for ele in path[:-1]:
214 | cur = cur.setdefault(ele, {})
215 | cur[path[-1]] = value
216 |
217 | if config['architecture'] not in ['x86_64', 'x86']:
218 | raise ValueError("only architectures supported are x86 and x86_64")
219 |
220 | visual_studio(config["vc_version"]) # forced set after args are evaluated
221 | config['__Default_environment'] = os.environ
222 | config['__environment'] = visual_studio_environment()
223 | config['__build_base_path'] = os.path.abspath(args.destination)
224 |
225 | if 'PYTHON' not in config['__environment']:
226 | config['__environment']['PYTHON'] = sys.executable
227 |
228 | def recursive_remove(graph, node):
229 | if not isinstance(graph.node[node]["task"], Project):
230 | for ancestor in graph.predecessors(node):
231 | recursive_remove(graph, ancestor)
232 | graph.remove_node(node)
233 |
234 |
235 | def main():
236 | time_format = "%(asctime)-15s %(message)s"
237 | logging.basicConfig(format=time_format, level=logging.DEBUG)
238 |
239 | parser = argparse.ArgumentParser()
240 | parser.add_argument('-f', '--file', default='makefile.uni.py', help='sets the build script')
241 | parser.add_argument('-d', '--destination', default='.', help='output directory (base for download and build)')
242 | parser.add_argument('-s', '--set', action='append', help='set configuration parameters')
243 | parser.add_argument('-g', '--graph', action='store_true', help='update dependency graph')
244 | parser.add_argument('-b', '--builddir', default='build', help='update build directory')
245 | parser.add_argument('-p', '--progressdir', default='progress', help='update progress directory')
246 | parser.add_argument('-i', '--installdir', default='install', help='update progress directory')
247 | parser.add_argument('target', nargs='*', help='make target')
248 | args = parser.parse_args()
249 |
250 | init_config(args)
251 |
252 | for d in ["download", "build", "progress","install"]:
253 | if not os.path.exists(config["paths"][d]):
254 | os.makedirs(config["paths"][d])
255 |
256 | logging.debug("building dependency graph")
257 | manager = TaskManager()
258 | imp.load_source(args.builddir, args.file)
259 | build_graph = manager.create_graph({})
260 | assert isinstance(build_graph, nx.DiGraph)
261 |
262 | if args.graph:
263 | draw_graph(build_graph, "graph")
264 |
265 | cycles = list(nx.simple_cycles(build_graph))
266 | if cycles:
267 | logging.error("There are cycles in the build graph")
268 | for cycle in cycles:
269 | logging.info(", ".join(cycle))
270 | return 1
271 |
272 | if args.target:
273 | for target in args.target:
274 | manager.enable(build_graph, target)
275 | else:
276 | manager.enable_all(build_graph)
277 |
278 | logging.debug("processing tasks")
279 | independent = extract_independent(build_graph)
280 |
281 | while independent:
282 | for node in independent:
283 | task = build_graph.node[node]['task']
284 | try:
285 | task.prepare()
286 | if build_graph.node[node]['enable'] and not task.already_processed():
287 | progress = Progress()
288 | progress.set_change_callback(progress_callback)
289 | if isinstance(task, Project):
290 | logging.debug("finished project \"{}\"".format(node))
291 | else:
292 | logging.debug("run task \"{}\"".format(node))
293 | if task.process(progress):
294 | task.mark_success()
295 | else:
296 | if task.fail_behaviour == Task.FailBehaviour.FAIL:
297 | logging.critical("task %s failed", node)
298 | exitcode = 1
299 | return 1
300 | elif task.fail_behaviour == Task.FailBehaviour.SKIP_PROJECT:
301 | recursive_remove(build_graph, node)
302 | break
303 | elif task.fail_behaviour == Task.FailBehaviour.CONTINUE:
304 | # nothing to do
305 | pass
306 | sys.stdout.write("\n")
307 | except Exception, e:
308 | logging.error("Task {} failed: {}".format(task.name, e))
309 | raise
310 |
311 | build_graph.remove_node(node)
312 |
313 | independent = extract_independent(build_graph)
314 |
315 |
316 | if __name__ == "__main__":
317 | try:
318 | exitcode = main()
319 | if not exitcode == 0:
320 | sys.exit(exitcode)
321 | except Exception, e:
322 | traceback.print_exc(file=sys.stdout)
323 | sys.exit(1)
324 |
325 |
326 |
--------------------------------------------------------------------------------
/makefile.uni.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2015 Sebastian Herbord. All rights reserved.
2 | #
3 | # This file is part of Mod Organizer.
4 | #
5 | # Mod Organizer is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Mod Organizer is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Mod Organizer. If not, see .
17 |
18 |
19 | from unibuild import Project
20 | from unibuild.modules import github, cmake, Patch, git, hg, msbuild, build, dummy
21 | from unibuild.utility import lazy, FormatDict
22 | from config import config
23 | from functools import partial
24 | from string import Formatter
25 | import os, sys
26 |
27 |
28 | """
29 | Settings
30 | """
31 |
32 | loot_version = "0.10.3"
33 | commit_id = "g0fcf788"
34 |
35 | """
36 | Projects
37 | """
38 |
39 |
40 | from unibuild.projects import sevenzip, qt5, boost, zlib, python, sip, pyqt5, ncc
41 | from unibuild.projects import asmjit, udis86, googletest, spdlog, fmtlib, lz4, WixToolkit
42 |
43 | # TODO modorganizer-lootcli needs an overhaul as the api has changed alot
44 | def bitness():
45 | return "x64" if config['architecture'] == "x86_64" else "Win32"
46 |
47 | Project("LootApi") \
48 | .depend(Patch.Copy("loot_api.dll".format(loot_version, commit_id), os.path.join(config["paths"]["install"], "bin", "loot"))
49 | .depend(github.Release("loot", "loot", loot_version, "loot-api_{}-0-{}_dev_{}".format(loot_version, commit_id, bitness()),"7z",tree_depth=1)
50 | .set_destination("lootapi"))
51 | )
52 |
53 |
54 | tl_repo = git.SuperRepository("modorganizer_super")
55 |
56 | def gen_userfile_content(project):
57 | with open("CMakeLists.txt.user.template", 'r') as f:
58 | res = Formatter().vformat(f.read(), [], FormatDict({
59 | 'build_dir' : project['edit_path'],
60 | 'environment_id': config['qt_environment_id'],
61 | 'profile_name' : config['qt_profile_name'],
62 | 'profile_id' : config['qt_profile_id']
63 | }))
64 | return res
65 |
66 |
67 | cmake_parameters = [
68 | "-DCMAKE_BUILD_TYPE={}".format(config["build_type"]),
69 | "-DDEPENDENCIES_DIR={}".format(config["paths"]["build"]),
70 | # boost git version "-DBOOST_ROOT={}/build/boostgit",
71 | "-DBOOST_ROOT={}/boost_{}".format(config["paths"]["build"], config["boost_version"].replace(".", "_")),
72 | ]
73 |
74 |
75 | if config.get('optimize', False):
76 | cmake_parameters.append("-DOPTIMIZE_LINK_FLAGS=\"/LTCG /INCREMENTAL:NO /OPT:REF /OPT:ICF\"")
77 |
78 |
79 | usvfs = Project("usvfs")
80 |
81 | usvfs.depend(cmake.CMake().arguments(cmake_parameters +
82 | ["-DCMAKE_INSTALL_PREFIX:PATH={}".format(config["paths"]["install"])] +
83 | ["-DPROJ_ARCH={}".format("x86" if config['architecture'] == 'x86' else "x64")])
84 | .install()
85 | # TODO Not sure why this is required, will look into it at a later stage once we get the rest to build
86 | .depend(github.Source(config['Main_Author'], "usvfs", "master")
87 | .set_destination("usvfs"))
88 | .depend("AsmJit")
89 | .depend("Udis86")
90 | .depend("GTest")
91 | .depend("fmtlib")
92 | .depend("spdlog")
93 | .depend("boost")
94 | )
95 |
96 |
97 | if config['architecture'] == 'x86_64':
98 | usvfs_32 = Project("usvfs_32")
99 | usvfs_32.depend(build.Run_With_Output(r'"{0}" unimake.py -d "{1}" --set architecture="x86" -b "build_32" -p "progress_32" -i "install_32" usvfs'.format(sys.executable,config['__build_base_path']),
100 | name="Building usvfs 32bit Dll",environment=config['__Default_environment'],working_directory=os.path.join(os.getcwd())))
101 | else:
102 | usvfs_32 = Project("usvfs_32")
103 | usvfs_32.depend(dummy.Success("usvfs_32"))
104 |
105 | for author, git_path, path, branch, dependencies, Build in [
106 | (config['Main_Author'], "modorganizer-game_features", "game_features", "master", [],False),
107 | (config['Main_Author'], "modorganizer-archive", "archive", "master", ["7zip", "Qt5"],True),
108 | (config['Main_Author'], "modorganizer-uibase", "uibase", "QT5.7", ["Qt5", "boost"],True),
109 | (config['Main_Author'], "modorganizer-lootcli", "lootcli", "master", ["LootApi", "boost"],True),
110 | (config['Main_Author'], "modorganizer-esptk", "esptk", "master", ["boost"],True),
111 | (config['Main_Author'], "modorganizer-bsatk", "bsatk", "master", ["zlib","boost"],True),
112 | (config['Main_Author'], "modorganizer-nxmhandler", "nxmhandler", "master", ["Qt5"],True),
113 | (config['Main_Author'], "modorganizer-helper", "helper", "master", ["Qt5"],True),
114 | (config['Main_Author'], "modorganizer-game_gamebryo", "game_gamebryo", "new_vfs_library", ["Qt5", "modorganizer-uibase",
115 | "modorganizer-game_features", "lz4"],True),
116 | (config['Main_Author'], "modorganizer-game_oblivion", "game_oblivion", "master", ["Qt5", "modorganizer-uibase",
117 | "modorganizer-game_gamebryo",
118 | "modorganizer-game_features"],True),
119 | (config['Main_Author'], "modorganizer-game_fallout3", "game_fallout3", "master", ["Qt5", "modorganizer-uibase",
120 | "modorganizer-game_gamebryo",
121 | "modorganizer-game_features"],True),
122 | (config['Main_Author'], "modorganizer-game_fallout4", "game_fallout4", "master", ["Qt5", "modorganizer-uibase",
123 | "modorganizer-game_gamebryo",
124 | "modorganizer-game_features"],True),
125 | (config['Main_Author'], "modorganizer-game_falloutnv", "game_falloutnv", "master", ["Qt5", "modorganizer-uibase",
126 | "modorganizer-game_gamebryo",
127 | "modorganizer-game_features"],True),
128 | (config['Main_Author'], "modorganizer-game_skyrim", "game_skyrim", "master", ["Qt5", "modorganizer-uibase",
129 | "modorganizer-game_gamebryo",
130 | "modorganizer-game_features"],True),
131 | ("LePresidente", "modorganizer-game_skyrimSE", "game_skyrimse", "dev", ["Qt5", "modorganizer-uibase",
132 | "modorganizer-game_gamebryo",
133 | "modorganizer-game_features"],True),
134 | (config['Main_Author'], "modorganizer-tool_inieditor", "tool_inieditor", "master", ["Qt5", "modorganizer-uibase"],True),
135 | (config['Main_Author'], "modorganizer-tool_inibakery", "tool_inibakery", "master", ["modorganizer-uibase"],True),
136 | (config['Main_Author'], "modorganizer-tool_configurator", "tool_configurator", "QT5.7", ["PyQt5"],True),
137 | (config['Main_Author'], "modorganizer-preview_base", "preview_base", "master", ["Qt5", "modorganizer-uibase"],True),
138 | (config['Main_Author'], "modorganizer-diagnose_basic", "diagnose_basic", "master", ["Qt5", "modorganizer-uibase"],True),
139 | (config['Main_Author'], "modorganizer-check_fnis", "check_fnis", "master", ["Qt5", "modorganizer-uibase"],True),
140 | (config['Main_Author'], "modorganizer-installer_bain", "installer_bain", "QT5.7", ["Qt5", "modorganizer-uibase"],True),
141 | (config['Main_Author'], "modorganizer-installer_manual", "installer_manual", "QT5.7", ["Qt5", "modorganizer-uibase"],True),
142 | (config['Main_Author'], "modorganizer-installer_bundle", "installer_bundle", "master", ["Qt5", "modorganizer-uibase"],True),
143 | (config['Main_Author'], "modorganizer-installer_quick", "installer_quick", "master", ["Qt5", "modorganizer-uibase"],True),
144 | (config['Main_Author'], "modorganizer-installer_fomod", "installer_fomod", "master", ["Qt5", "modorganizer-uibase"],True),
145 | (config['Main_Author'], "modorganizer-installer_ncc", "installer_ncc", "master", ["Qt5", "modorganizer-uibase", "NCC"],True),
146 | (config['Main_Author'], "modorganizer-bsa_extractor", "bsa_extractor", "master", ["Qt5", "modorganizer-uibase"],True),
147 | (config['Main_Author'], "modorganizer-plugin_python", "plugin_python", "master", ["Qt5", "boost", "Python", "modorganizer-uibase",
148 | "sip"],True),
149 | (config['Main_Author'], "githubpp", "githubpp", "master", ["Qt5"],True),
150 | (config['Main_Author'], "modorganizer", "modorganizer", "QT5.7", ["Qt5", "boost", "usvfs_32",
151 | "modorganizer-uibase", "modorganizer-archive",
152 | "modorganizer-bsatk", "modorganizer-esptk",
153 | "modorganizer-game_features",
154 | "usvfs","githubpp", "NCC"], True),
155 | ]:
156 | build_step = cmake.CMake().arguments(cmake_parameters +
157 | ["-DCMAKE_INSTALL_PREFIX:PATH={}".format(config["paths"]["install"])])\
158 | .install()
159 |
160 | for dep in dependencies:
161 | build_step.depend(dep)
162 |
163 | project = Project(git_path)
164 |
165 | if Build:
166 | project.depend(build_step.depend(github.Source(author, git_path, branch, super_repository=tl_repo)
167 | .set_destination(path)))
168 | else:
169 | project.depend(github.Source(author, git_path, branch, super_repository=tl_repo)
170 | .set_destination(path))
171 |
172 |
173 |
174 | def python_zip_collect(context):
175 | import libpatterns
176 | import glob
177 | from zipfile import ZipFile
178 |
179 | ip = os.path.join(config["paths"]["install"], "bin")
180 | bp = python.python['build_path']
181 |
182 | with ZipFile(os.path.join(ip, "python27.zip"), "w") as pyzip:
183 | for pattern in libpatterns.patterns:
184 | for f in glob.iglob(os.path.join(bp, pattern)):
185 | pyzip.write(f, f[len(bp):])
186 |
187 | return True
188 |
189 |
190 | Project("python_zip") \
191 | .depend(build.Execute(python_zip_collect)
192 | .depend("Python")
193 | )
194 |
195 | if config['Installer']:
196 | #build_installer = cmake.CMake().arguments(cmake_parameters +["-DCMAKE_INSTALL_PREFIX:PATH={}/installer".format(config["__build_base_path"])]).install()
197 | wixinstaller = Project("WixInstaller")
198 |
199 | wixinstaller.depend(github.Source(config['Main_Author'],"modorganizer-WixInstaller", "VSDev", super_repository=tl_repo)
200 | .set_destination("WixInstaller"))\
201 | .depend("modorganizer").depend("usvfs").depend("usvfs_32")
202 |
203 |
204 |
--------------------------------------------------------------------------------
/unibuild/modules/build.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2015 Sebastian Herbord. All rights reserved.
2 | #
3 | # This file is part of Mod Organizer.
4 | #
5 | # Mod Organizer is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Mod Organizer is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Mod Organizer. If not, see .
17 |
18 |
19 | from unibuild import Task
20 | from unibuild.builder import Builder
21 | from unibuild.utility.lazy import Lazy
22 | from subprocess import Popen
23 | from config import config
24 | import os.path
25 | import logging
26 |
27 | STATIC_LIB = 1
28 | SHARED_LIB = 2
29 | EXECUTABLE = 3
30 |
31 |
32 | class CPP(Builder):
33 |
34 | def __init__(self, cflags=None):
35 | super(CPP, self).__init__()
36 | self.__type = EXECUTABLE
37 | self.__targets = []
38 | self.__cflags = cflags or ["-nologo", "-O2", "-MD"]
39 |
40 | @property
41 | def name(self):
42 | if self._context is None:
43 | return "custom build"
44 | else:
45 | return "custom build {0}".format(self._context.name)
46 |
47 | def fulfilled(self):
48 | return False
49 |
50 | def type(self, build_type):
51 | self.__type = build_type
52 | return self
53 |
54 | def __gen_build_cmd(self, target, files):
55 | if self.__type == STATIC_LIB:
56 | return "link.exe /lib /nologo /out:{0}.lib {1}".format(
57 | target, " ".join([self.__to_obj(f) for f in files]))
58 | else:
59 | raise NotImplementedError("type {} not yet implemented", self.__type)
60 |
61 | def sources(self, target, files, top_level=True):
62 | self.__targets.append((target, files, self.__gen_build_cmd(target, files), top_level))
63 | return self
64 |
65 | def custom(self, target, dependencies=None, cmd=None, top_level=False):
66 | self.__targets.append((target, dependencies, cmd, top_level))
67 | return self
68 |
69 | @staticmethod
70 | def __to_obj(filename):
71 | return "{}.obj".format(os.path.splitext(os.path.basename(filename))[0])
72 |
73 | def gen_makefile(self, path):
74 | with open(os.path.join(path, "unimakefile"), "w") as mf:
75 | mf.write("CFLAGS={}\n\n".format(" ".join(self.__cflags)))
76 | for target in self.__targets:
77 | files = target[1] or []
78 | for f in files:
79 | mf.write("{}:\n".format(self.__to_obj(f)))
80 | mf.write("\t{cl} -c $(CFLAGS) -Fo {file}\n\n".format(cl="cl", file=f))
81 |
82 | mf.write("{0}: {1}\n\t{2}\n\n".format(target[0],
83 | " ".join([self.__to_obj(f) for f in files]),
84 | target[2] or ""))
85 |
86 | mf.write("all: {}\n".format(" ".join([target[0]
87 | for target in self.__targets
88 | if target[3]])))
89 |
90 | def process(self, progress):
91 | path = self._context["build_path"]
92 |
93 | self.gen_makefile(path)
94 |
95 | soutpath = os.path.join(self._context["build_path"], "stdout.log")
96 | serrpath = os.path.join(self._context["build_path"], "stderr.log")
97 | with open(soutpath, "a") as sout:
98 | with open(serrpath, "a") as serr:
99 | command = "{} /f unimakefile all".format(config["tools"]["make"])
100 | sout.write("running {} in {}\n".format(command, self._context['build_path']))
101 | proc = Popen(command,
102 | env=config["__environment"],
103 | cwd=self._context["build_path"],
104 | shell=True,
105 | stdout=sout, stderr=serr)
106 | proc.communicate()
107 | if proc.returncode != 0:
108 | logging.error("failed to build custom makefile (returncode %s), see %s and %s",
109 | proc.returncode, soutpath, serrpath)
110 | return False
111 | return True
112 |
113 |
114 | class Install(Builder):
115 | def __init__(self, make_tool=None):
116 | super(Install, self).__init__()
117 | self.__make_tool = Lazy(make_tool or config['tools']['make'])
118 |
119 | @property
120 | def name(self):
121 | if self._context is None:
122 | return "make install"
123 | else:
124 | return "make install {0}".format(self._context.name)
125 |
126 | def process(self, progress):
127 | if "build_path" not in self._context:
128 | logging.error("source path not known for {},"
129 | " are you missing a matching retrieval script?".format(self.name()))
130 | soutpath = os.path.join(self._context["build_path"], "stdout.log")
131 | serrpath = os.path.join(self._context["build_path"], "stderr.log")
132 |
133 | with open(soutpath, "a") as sout:
134 | with open(serrpath, "a") as serr:
135 | proc = Popen([self.__make_tool(), "install"],
136 | shell=True,
137 | env=config["__environment"],
138 | cwd=self._context["build_path"],
139 | stdout=sout, stderr=serr)
140 | proc.communicate()
141 | if proc.returncode != 0:
142 | logging.error("failed to install (returncode %s), see %s and %s",
143 | proc.returncode, soutpath, serrpath)
144 | return False
145 | return True
146 |
147 |
148 | class Make(Builder):
149 | def __init__(self, make_tool=None, environment=None, working_directory=None):
150 | super(Make, self).__init__()
151 | self.__install = False
152 | self.__make_tool = Lazy(make_tool or config['tools']['make'])
153 | self.__environment = Lazy(environment)
154 | self.__working_directory = Lazy(working_directory)
155 |
156 | @property
157 | def name(self):
158 | if self._context is None:
159 | return "make"
160 | else:
161 | return "make {0}".format(self._context.name)
162 |
163 | def install(self):
164 | self.__install = True
165 | return self
166 |
167 | def process(self, progress):
168 | if "build_path" not in self._context:
169 | logging.error("source path not known for {},"
170 | " are you missing a matching retrieval script?".format(self.name()))
171 | soutpath = os.path.join(self._context["build_path"], "stdout.log")
172 | serrpath = os.path.join(self._context["build_path"], "stderr.log")
173 |
174 | with open(soutpath, "a") as sout:
175 | with open(serrpath, "a") as serr:
176 | environment = dict(self.__environment()
177 | if self.__environment() is not None
178 | else config["__environment"])
179 | cwd = str(self.__working_directory()
180 | if self.__working_directory() is not None
181 | else self._context["build_path"])
182 |
183 | proc = Popen(self.__make_tool().split(" "),
184 | env=environment,
185 | cwd=cwd,
186 | shell=True,
187 | stdout=sout, stderr=serr)
188 | proc.communicate()
189 | if proc.returncode != 0:
190 | logging.error("failed to run make (returncode %s), see %s and %s",
191 | proc.returncode, soutpath, serrpath)
192 | return False
193 |
194 | if self.__install:
195 | proc = Popen([config['tools']['make'], "install"],
196 | shell=True,
197 | env=environment,
198 | cwd=cwd,
199 | stdout=sout, stderr=serr)
200 | proc.communicate()
201 | if proc.returncode != 0:
202 | logging.error("failed to install (returncode %s), see %s and %s",
203 | proc.returncode, soutpath, serrpath)
204 | return False
205 |
206 | return True
207 |
208 |
209 | class Execute(Builder):
210 | def __init__(self, function, name=None):
211 | super(Execute, self).__init__()
212 | self.__function = function
213 | self.__name = name
214 |
215 | @property
216 | def name(self):
217 | if self._context is None:
218 | return "execute {}".format(self.__name or self.__function.func_name)
219 | else:
220 | return "execute {}_{}".format(self._context.name, self.__name or self.__function.func_name)
221 |
222 | def process(self, progress):
223 | return self.__function(context=self._context)
224 |
225 |
226 | class Run(Builder):
227 | def __init__(self, command, fail_behaviour=Task.FailBehaviour.FAIL, environment=None, working_directory=None,
228 | name=None):
229 | super(Run, self).__init__()
230 | self.__command = Lazy(command)
231 | self.__name = name
232 | self.__fail_behaviour = fail_behaviour
233 | self.__environment = Lazy(environment)
234 | self.__working_directory = Lazy(working_directory)
235 |
236 | @property
237 | def name(self):
238 | if self.__name:
239 | return "run {}".format(self.__name)
240 | else:
241 | return "run {}".format(self.__command.peek().split()[0]).replace("\\", "/")
242 |
243 | def process(self, progress):
244 | if "build_path" not in self._context:
245 | logging.error("source path not known for {},"
246 | " are you missing a matching retrieval script?".format(self.name))
247 |
248 | soutpath = os.path.join(self._context["build_path"], "stdout.log")
249 | serrpath = os.path.join(self._context["build_path"], "stderr.log")
250 | with open(soutpath, "w") as sout:
251 | with open(serrpath, "w") as serr:
252 | environment = dict(self.__environment()
253 | if self.__environment() is not None
254 | else config["__environment"])
255 | cwd = str(self.__working_directory()
256 | if self.__working_directory() is not None
257 | else self._context["build_path"])
258 |
259 | sout.write("running {} in {}".format(self.__command(), cwd))
260 | proc = Popen(self.__command(),
261 | env=environment,
262 | cwd=cwd,
263 | shell=True,
264 | stdout=sout, stderr=serr)
265 | proc.communicate()
266 | if proc.returncode != 0:
267 | logging.error("failed to run %s (returncode %s), see %s and %s",
268 | self.__command(), proc.returncode, soutpath, serrpath)
269 | return False
270 |
271 | return True
272 |
273 | class Run_With_Output(Builder):
274 | def __init__(self, command, fail_behaviour=Task.FailBehaviour.FAIL, environment=None, working_directory=None,
275 | name=None):
276 | super(Run_With_Output, self).__init__()
277 | self.__command = Lazy(command)
278 | self.__name = name
279 | self.__fail_behaviour = fail_behaviour
280 | self.__environment = Lazy(environment)
281 | self.__working_directory = Lazy(working_directory)
282 |
283 | @property
284 | def name(self):
285 | if self.__name:
286 | return "run {}".format(self.__name)
287 | else:
288 | return "run {}".format(self.__command.peek().split()[0]).replace("\\", "/")
289 |
290 | def process(self, progress):
291 |
292 | environment = dict(self.__environment()
293 | if self.__environment() is not None
294 | else config["__environment"])
295 | cwd = str(self.__working_directory()
296 | if self.__working_directory() is not None
297 | else self._context["build_path"])
298 |
299 |
300 | proc = Popen(self.__command(),
301 | env=environment,
302 | cwd=cwd,
303 | shell=True)
304 | proc.communicate()
305 | if proc.returncode != 0:
306 | if isinstance(proc.returncode , (str, unicode)):
307 | logging.error("failed to run %s (returncode %s), see %s and %s",
308 | self.__command(), proc.returncode)
309 | return False
310 | else:
311 | logging.error("failed to run {} (returncode {})".format(self.__command(), proc.returncode))
312 | return False
313 |
314 | return True
315 |
--------------------------------------------------------------------------------