This is an experimental copy for testing Poikilos' issue mirroring system. Note that Gitea's migration tool can import issues, but the "Issues" checkbox is disabled when "This repository will be a mirror" is enabled (it is for this repo).
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1215 lines
41 KiB

#!/usr/bin/env python3
"""
---------------------------------------------------------------------
file information
---------------------------------------------------------------------
Name: This program is based on mtcompile-program.pl
Purpose: Linux Minetest build script
License: Creative Commons Attribution-NonCommercial-ShareAlike 4.0.
Attribution: OldCoder (Robert Kiraly)
and Poikilos (Jake Gustafson)
Revision: See program parameters section
---------------------------------------------------------------------
important note
---------------------------------------------------------------------
This software is provided on an AS IS basis with ABSOLUTELY NO WAR-
RANTY. The entire risk as to the quality and performance of the
software is with you. Should the software prove defective, you as-
sume the cost of all necessary servicing, repair or correction. In
no event will any of the developers, or any other party, be liable
to anyone for damages arising out of use of the software, or inabil-
ity to use the software.
---------------------------------------------------------------------
overview
---------------------------------------------------------------------
"""
import sys
import subprocess
import shutil
import re
import tarfile
import stat
from zipfile import ZipFile
import zipfile
import platform
import os
def error(msg):
sys.stderr.write(msg + "\n")
def customExit(msg):
error(msg)
exit(1)
def isExecutableFile(path):
return os.path.isfile(path) and os.access(path, os.X_OK)
def which(prog):
"""
Check for the given file within os.environ["PATH"], which contains
paths separated by os.pathsep.
NOTE: sys.path is NOT applicable, since it only has Python paths.
"""
for thisPath in os.environ["PATH"].split(os.pathsep):
sub_path = os.path.join(thisPath, prog)
if os.path.isfile(sub_path):
return sub_path
return ""
def zipdir(path, ziph):
"""
Zip an entire directory.
See Mark Byers' Dec 6, 2009 answer edited by JosephH Feb 24, 2016
on <https://stackoverflow.com/questions/1855095/how-to-create-a-zip-
archive-of-a-directory-in-python>
"""
# ziph is zipfile handle
for root, dirs, files in os.walk(path):
for file in files:
ziph.write(os.path.join(root, file))
def endsWithAny(haystack, needles):
for needle in needles:
if haystack.endswith(needle):
return True
return False
def containsAny(haystack, needles):
for needle in needles:
if haystack.contains(needle):
return True
return False
def startsWithAny(haystack, needles):
for needle in needles:
if haystack.startswith(needle):
return True
return False
# Label must be single-quoted here
USAGE_TEXT = """
Usage: {PROGNAME} --options --build
The "--build" switch is required. It needs to be specified on the com-
mand line or you'll get this usage text. The other switches are opt-
ional.
The command-line argument "build", specified without dashes, will also
work.
----------------------------------------------------------------------
Background information related to "builds":
1. Minetest, or this version, uses a combined source and production
tree. I.e., a single tree can serve both purposes until it's cleaned
up for distribution.
2. The production tree is portable in the sense that it can be moved
to different directories on a given system and the program will still
work.
3. The production tree isn't portable in the sense that it'll run on
different systems unless the "--portable" or "--makeprod" option swi-
tch os used.
4. By default, this script deletes the source tree on each run, un-
packs an included source tarball, and deletes unneeded "build" files
after a build is completed. The last step results in a pure production
as opposed to source tree.
Command-line option switches can be used to modify this behavior.
Examples include "--noclean" and "--gitreset".
----------------------------------------------------------------------
Option switches:
--noclean # If --noclean is specified, this script tries to re-
use the existing source tree, if there is one, and
doesn't deleted "build" files afterward.
The "--git*" and "--debug" switches imply this swi-
tch.
Aliases: --notidy
--server # Build server & not client unless --client also
--client # Build client & not server unless --server also
Default: If neither is set, both are implied
If just one is set, the other is off
--postgresql # Enable PostgreSQL (requires installed copy)
Aliases: --postgres
--redis # Enable Redis (requires installed copy)
--debug # Build a "debug" version of the program suitable for
use with "gdb".
--makeprod # Build a portable production release (ZIP file) of
Linux Minetest. This is only needed by people who
wish to redistribute the program. The switch implies
--portable. It isn't compatible with --noclean or
--debug.
--portable # Build a portable version. If this isn't specified,
the copy of Minetest built is tailored to your ma-
chine and may only run on an identical machine (same
hardware, distro, and distro release). At the same
time, non-portable versions may be slightly faster.
--gitreset # Delete any existing source tree and try to do a
fresh "git clone".
--gitpull # Try to update the current source tree using "git
pull". If there is no source tree or it's not a
"git" tree, this switch is the same as "--gitreset".
The "git" switches require both the "git" software
package and Internet access.
--safe # Don't delete existing source trees automatically.
--edgy # Build EdgyTest instead of Final Minetest. Implies
"--fakemt4".
--fakemt4 # Pretend to be MT 4. Implies "--oldproto".
--oldproto # Limit network protocol used to level 32. For the mo-
ment, this is the default mode and there is no way
to disable it.
--help # Display usage text and exit.
Aliases: --usage
For full documentation, see "linux-minetest-kit.txt".
--MT_SRC=<minetest> # Set the minetest source path to a
# locally-modified copy, such as
# $HOME/git/minetest
Before using this script, please
see doc/mtcompile-program-local.md in EnlivenMinetest
for changes and progress on implementing features from
mtcompile-program.pl.
"""
#---------------------------------------------------------------------
# module setup
#---------------------------------------------------------------------
# TODO: Trap warnings to mimic the Perl version?
#SIG["__WARN__"] = sub { die @_; }
#---------------------------------------------------------------------
# program parameters
#---------------------------------------------------------------------
def streamEdit(inPath, replacements):
"""
Replace parts similarly to sed ("stream editor").
See David Miller's Dec 13, 2010 answer edited by dpb Jul 23, 2013
on <https://stackoverflow.com/questions/4427542/how-to-do-sed-like-
text-replace-with-python>
Sequential arguments:
inPath -- Edit then save the file at this path.
replacements -- a replacements dict where the key is the string or
regex, and the value is the new string to use in cases of]
matches.
"""
with open(inPath, "r") as sources:
lines = sources.readlines()
with open(inPath, "w") as sources:
for rawLine in lines:
line = rawLine
for k, v in replacements.items():
line = re.sub(k, v, line)
sources.write(line)
def _UNUSED_evalSed(line, sedStr):
"""
Mimic sed.
Example:
To mimic Perl `$str =~ s@\s+@ @gs;` do `evalSed(str, "s@\s+@ @gs")`.
For m/, use re.findall() instead.
"""
# - See <https://stackoverflow.com/questions/8903180/how-to-use-sed-
# without-a-file-with-an-env-var>
# - See <https://stackoverflow.com/questions/3503879/assign-output-
# of-os-system-to-a-variable-and-prevent-it-from-being-displayed-
# on>
return os.popen('echo "{}" | sed "{}"'.format(line, sedStr)).read()
PURPOSE = 'Linux Minetest build script'
REVISION = '200522' # version of this script, not linux-minetest-kit
PROFILE_DIR = None
HOME_V = "HOME"
if platform.system() == "Windows":
HOME_V = "USERPROFILE"
PROFILE_DIR = os.environ.get(HOME_V)
EXTRACTED_DIR = None
if os.path.isdir("mtsrc"):
EXTRACTED_DIR = os.getcwd()
elif PROFILE_DIR is not None:
EXTRACTED_DIR = os.path.join(PROFILE_DIR, ".config",
"EnlivenMinetest",
"linux-minetest-kit")
if not os.path.isdir(EXTRACTED_DIR):
print("You must run this from the directory of the extracted")
print("linux-minetest-kit or have {}".format(EXTRACTED_DIR))
exit(1)
else:
if not os.path.isdir(EXTRACTED_DIR):
print("You must run this from the directory of the extracted"
"linux-minetest-kit or have a {} variable"
"".format(HOME_V))
exit(1)
GITURL = 'http://git.minetest.org/minetest/minetest.git'
IE = 'Internal error'
Flags = {}
#---------------------------------------------------------------------
# global variables
#---------------------------------------------------------------------
PROGNAME = None # Program name without path
DirStack = [os.getcwd()] # Directory stack
#---------------------------------------------------------------------
# used by "--oldproto"
#---------------------------------------------------------------------
segment = """
set(VERSION_MAJOR 0)
set(VERSION_MINOR 4)
set(VERSION_PATCH 17)
set(VERSION_TWEAK 1)
set(VERSION_EXTRA "" CACHE STRING "Stuff to append to version string")
# Change to false for releases
set(DEVELOPMENT_BUILD False)
set(VERSION_STRING "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}.${VERSION_TWEAK}")
if VERSION_EXTRA:
set(VERSION_STRING ${VERSION_STRING}-${VERSION_EXTRA})
elseif(DEVELOPMENT_BUILD)
set(VERSION_STRING "${VERSION_STRING}-dev")
endif()
if CMAKE_BUILD_TYPE STREQUAL Debug:
"""
#---------------------------------------------------------------------
# low-level utility routines
#---------------------------------------------------------------------
def pushd(path):
if not os.path.isdir(path):
print("[pushd] ERROR: \"{}\" does not exist.".format(path))
exit(1)
os.chdir(path)
DirStack.append(path)
def popd():
if len(DirStack) < 2:
print("[popd] ERROR: only the original path is on the stack")
print(" (you popped more than you pushed).")
exit(1)
else:
del DirStack[-1]
os.chdir(DirStack[-1])
def execute(cmd, shell=True):
"""
Iterate output of a command.
See tokland's Dec 11, 2010 answer on <https://stackoverflow.com/
questions/4417546/constantly-print-subprocess-output-while-
process-is-running>
"""
popen = subprocess.Popen(cmd, stdout=subprocess.PIPE,
universal_newlines=True,
shell=shell)
for stdout_line in iter(popen.stdout.readline, ""):
yield stdout_line
popen.stdout.close()
return_code = popen.wait()
if return_code:
raise subprocess.CalledProcessError(return_code, cmd)
def RunCmd(cmdParts, exitOnFail=True, shell=True):
# Popen can get confused when arguments contain quotes
# (see <https://stackoverflow.com/questions/14928860/passing-double-
# quote-shell-commands-in-python-to-subprocess-popen>) such as
# in "-DCMAKE_CXX_FLAG="+XCFLAGS where XCFLAGS contains quotes.
# child = subprocess.Popen(cmdParts, shell=shell,
# stdout=subprocess.PIPE,
# universal_newlines=True)
# streamdata = child.communicate()[0]
# rc = child.returncode
# Instead of communicate, read the lines (See def execute)
# if rc != 0:
# if exitOnFail:
# exit(rc)
# else:
# print("WARNING: {} failed".format(' '.join(cmdParts)))
try:
for line in execute(cmdParts, shell=shell):
print(line, end="")
except subprocess.CalledProcessError as ex:
msg = ("The process '{}' ended with error code {}."
"".format(" ".join(ex.cmd), ex.returncode))
if exitOnFail:
print("ERROR: " + msg)
exit(ex.returncode)
else:
print("WARNING: " + msg)
def FixStr(s):
# TODO: make sure this does everything the original does
# (see mtcompile-program.pl)
if s is None:
return ""
return s.strip()
def GetProgDir():
# TODO: finish converting this from Perl
# - [x] set PROGNAME
global PROGNAME
PROGNAME = os.path.basename(__file__)
return os.getcwd()
def GetOptions(nonBoolNames=[], bareArgs=[]):
"""
Convert command-line arguments to values in the global Flags dict.
Keyword Arguments:
nonBoolNames -- Specify which arguments can have values. All others
are considered boolean, and will end the program if they contain
'='.
bareArgs -- allowed arguments without "--" at the beginning.
Returns:
false if arg has an equal sign and preceding part after "--" is not
in nonBoolNames.
"""
for i in range(1, len(sys.argv)):
arg = sys.argv[i]
if (arg in bareArgs) or arg.startswith("--"):
start = 0
if arg.startswith("--"):
start = 2
signI = arg.find("=")
name = arg[start:]
val = True
if signI > -1:
name = arg[start:signI]
if name not in nonBoolNames:
print("Only the following options can have values:"
" {}.".format(nonBoolNames))
return False
val = arg[signI+1:]
# if TitleCaseAndFlag:
# name = "Flag" + name.title()
print("* {} = {}".format(name, val))
Flags[name] = val
else:
print("The option is unknown: {}".format(arg))
return False
return True
USAGE_FMT = """
#{PROGNAME} {REVISION} - {PURPOSE}
#{USAGE_TEXT}
"""
def UsageText(msg=""):
"""
"UsageText" prints usage text for the current program, then termin-
ates the program with exit status one.
"""
# NOTE: PROGNAME is this script's name, NOT the engine name.
THIS_USAGE = USAGE_FMT.format(PROGNAME=PROGNAME,
REVISION=REVISION,
PURPOSE=PURPOSE,
USAGE_TEXT=USAGE_TEXT.format(
PROGNAME=PROGNAME
))
# USAGE_TEXT = evalSed('s@\s*\z@\n@s')
print(THIS_USAGE)
print("")
print(msg)
print("")
print("")
exit(1)
def main():
#---------------------------------------------------------------------
# Misc. variables.
#my $cmd; # Shell command string
#my $tmpStr; # Scratch
#---------------------------------------------------------------------
# Mode flags.
# These may be modified, indirectly, by command-line switches.
MAKEDEBUG = False
PORTABLE = False
TIDYUP = True
MAKEPROD = False
#---------------------------------------------------------------------
# Command-line option flags.
Flags["build"] = False
Flags["client"] = False
Flags["debug"] = False
Flags["edgy"] = False
Flags["gitpull"] = False
Flags["gitreset"] = False
Flags["help"] = False
Flags["makeprod"] = False
Flags["fakemt4"] = False
Flags["noclean"] = False
Flags["portable"] = False
Flags["postgres"] = False
Flags["redis"] = False
Flags["safe"] = False
Flags["server"] = False
Flags["oldproto"] = True
#---------------------------------------------------------------------
# Initial setup.
#select STDERR; $| = ONE; # Force STDERR flush on write
#select STDOUT; $| = ONE; # Force STDOUT flush on write
#---------------------------------------------------------------------
# Get absolute path for script directory.
# As a side effect, this function call initializes the global variable
# "$PROGNAME". Note that this must be done before "UsageText" is call-
# ed.
THISDIR = GetProgDir()
#---------------------------------------------------------------------
# Parse command-line arguments.
if not GetOptions(nonBoolNames=["MT_SRC"], bareArgs=["build"]):
UsageText()
mapLongArgs = {}
mapLongArgs["edgytest"] = "edgy"
mapLongArgs["notidy"] = "noclean"
mapLongArgs["oldprotocol"] = "oldproto"
mapLongArgs["postgresql"] = "postgres"
mapLongArgs["usage"] = "help"
for k, v in mapLongArgs.items():
old_v = Flags.get(k)
if old_v is not None:
Flags[v] = old_v
del Flags[k]
# Handle usage-text exit
if (not Flags["build"]) or Flags["help"]:
if not Flags["build"]:
msg = ("You did not specify build or --build (got:{})."
"".format(Flags))
UsageText(msg=msg)
#---------------------------------------------------------------------
# Handle misc. flag issues.
if Flags["edgy"]:
Flags["fakemt4"] = True
if Flags["edgy"] or Flags["fakemt4"]:
Flags["oldproto"] = True
if Flags["edgy"] and Flags["gitpull"]:
customExit("Error: Can't use both --edgy and --gitpull")
#---------------------------------------------------------------------
# Confirm that script is running in the right place.
if not os.path.isdir('mtsrc'):
error("""
Error: This script should be stored, and executed, in the directory
which contains the "mtsrc" directory.
""")
exit(1)
#---------------------------------------------------------------------
# Additional directory paths.
BALLDIR = os.path.join(THISDIR, "mtsrc", "newline")
PRODDIR = os.path.join(THISDIR, "minetest")
TOOLS_PREFIX = os.path.join(THISDIR, "toolstree")
BINDIR = os.path.join(TOOLS_PREFIX, "bin")
INCDIR = os.path.join(TOOLS_PREFIX, "include")
LIBDIR = os.path.join(TOOLS_PREFIX, "lib")
LIB64DIR = os.path.join(TOOLS_PREFIX, "lib64")
#---------------------------------------------------------------------
# Misc. setup.
if not BINDIR in os.environ["PATH"]:
os.environ["PATH"] = BINDIR + os.pathsep + os.environ["PATH"]
which("g++")
#---------------------------------------------------------------------
# Handle some of the option flags.
if Flags["debug"]:
MAKEDEBUG = True
if Flags["makeprod"]:
MAKEPROD = True
if Flags["noclean"]:
TIDYUP = False
if MAKEPROD:
MAKEDEBUG = False
PORTABLE = True
TIDYUP = True
#---------------------------------------------------------------------
# Handle "--gitreset".
if Flags["gitreset"]:
if Flags["gitpull"]:
customExit("Error: Can't use both --gitreset and --gitpull\n")
if Flags["safe"] and os.path.isdir('minetest'):
print("""
Error: "minetest" directory exists and "--gitreset" needs to delete
it. But can't because "--safe" was specified. If you wish to proceed,
move or rename the directory.
""")
exit(1);
TIDYUP = False
print("""
* --gitreset specified and --safe not specified
* Removing any existing "minetest" directory
""")
shutil.rmtree("minetest")
print("* Attempting a git clone...")
cmdParts = ["git", "clone", GITURL, "minetest"]
print(" " + " ".join(cmdParts))
RunCmd(cmdParts);
#---------------------------------------------------------------------
# Handle "--gitpull".
if Flags["gitpull"]:
if Flags["gitreset"]:
customExit("Error: Can't use both --gitreset and --gitpull\n")
TIDYUP = False
if os.path.isdir('minetest'):
if not os.path.isdir('minetest/.git'):
print("""
Error: "--gitpull" specified and I see a "minetest" directory but no
"minetest/.git" directory.
If you'd like to use "--gitpull", delete, rename, or move the "mine-
test" directory.
Or you can use "--gitreset" instead. This will delete the directory
automatically.
""")
exit(1)
if not os.path.isdir('minetest'):
print("""
* "--gitpull" specified but I don't see a "minetest" directory
* Attempting a git clone
""")
cmdParts = ["git", "clone", GITURL, "minetest"]
print("cmd " + " ".join(cmdParts))
RunCmd(cmdParts)
else:
if not os.path.isdir(os.path.join("minetest", ".git")):
customExit(IE + "#250458")
print("""
* --gitpull specified and I see "minetest/.git"
* Attempting a git pull
""")
pushd('minetest');
cmdParts = ["git", "pull"]
RunCmd(cmdParts, exit_on_fail=false)
popd();
#---------------------------------------------------------------------
# Handle "--client" and "--server".
client_line = "-DBUILD_CLIENT=1"
server_line = "-DBUILD_SERVER=1"
if Flags["client"] and not Flags["server"]:
client_line = "-DBUILD_CLIENT=1"
server_line = "-DBUILD_SERVER=0"
if not Flags["client"] and Flags["server"]:
client_line = "-DBUILD_CLIENT=0"
server_line = "-DBUILD_SERVER=1"
#---------------------------------------------------------------------
# Status messages.
NUBDF = "not used by default in this version"
print("""
* leveldb (by default)
* sqlite3 (by default)
""")
#---------------------------------------------------------------------
# Handle "--postgres".
postgres_line = "-DENABLE_POSTGRESQL=0"
if Flags["postgres"]:
print("* postgres (due to --postgresql)")
postgres_line = "-DENABLE_POSTGRESQL=1"
else:
print("(skipping postgresql --"+NUBDF)
#---------------------------------------------------------------------
# Handle "--redis".
redis_line = "-DENABLE_REDIS=0"
if Flags["redis"]:
print("* redis (due to --redis)")
redis_line = "-DENABLE_REDIS=1"
else:
print("(skipping redis --"+NUBDF)
#---------------------------------------------------------------------
# "--portable" requires the bootstrapped "gcc".
if PORTABLE and not os.path.isfile(os.path.join(BINDIR, "gcc")):
print("""
Error: For Linux portable mode (--portable), you need to build the in-
cluded "gcc" 8 compiler. To do so, run "mtcompile-libraries.sh" with
gcc-bootstrap mode enabled.
""")
exit(1)
#---------------------------------------------------------------------
# Identify "gcc" major release number.
#my ($GCCVER) = $tmpStr =~ m@\ngcc.* (\d+)*\.\d+\.@
tmpStr = subprocess.check_output(["gcc", "--version"]).decode()
tmpParts = re.findall("gcc.* (\d+)*\.\d+\.", tmpStr)
GCCVER = None
if len(tmpParts) > 0:
GCCVER = tmpParts[0]
else:
customExit("Error: Not able to identify gcc release")
#---------------------------------------------------------------------
# Replace existing "minetest" directory.
RESETDIR = (not os.path.isdir(PRODDIR)) or TIDYUP
if RESETDIR:
if Flags["safe"] and os.path.isdir('minetest'):
print("""
Error: We need to delete the existing "minetest" directory, but
"--safe" is specified. If you'd like to preserve the directory, move
or rename it. Otherwise, drop the "--safe" switch.
""")
exit(1)
# PRODDIR is THISDIR/minetest
print("* cleaning " + os.getcwd())
for sub in os.listdir(THISDIR):
sub_path = os.path.join(THISDIR, sub)
if sub.startswith("."):
continue
if os.path.isfile(sub_path):
continue
if sub_path == PRODDIR:
# ^ sub_path must be generated the same way as
# PRODDIR (from THISDIR) for this to work (to
# remove the linux-minetest-kit/minetest directory).
print("* removing old \"{}\"".format(sub_path))
shutil.rmtree(sub_path)
elif sub.startswith("minetest-newline"):
shutil.rmtree(sub_path)
print("* extracting in " + os.getcwd())
mtNewLine = "minetest-newline"
if Flags["edgy"]:
mtNewLine = "minetest-newline"
if Flags.get("MT_SRC") is not None:
if not os.path.isdir(Flags["MT_SRC"]):
customExit("{} does not exist.".format(Flags["MT_SRC"]))
print("* using \"{}\" (copying to \"{}\")"
"".format(Flags["MT_SRC"], PRODDIR))
shutil.copytree(Flags["MT_SRC"], PRODDIR,
copy_function=shutil.copy2)
else:
tarPath = os.path.join(BALLDIR, mtNewLine + ".tar.bz2")
tar = tarfile.open(tarPath)
tar.extractall(path=THISDIR)
tar.close()
for sub in os.listdir(THISDIR):
sub_path = os.path.join(THISDIR, sub)
if sub.startswith(mtNewLine):
print("* using {} as minetest".format(sub_path))
shutil.move(sub_path, PRODDIR)
break
os.chdir(PRODDIR)
# or die "$IE #505850\n";
#---------------------------------------------------------------------
# Sanity check.
if not os.path.isfile('CMakeLists.txt'):
print("""
Error: You're trying to build using a "minetest" directory that's mis-
sing a "CMakeLists.txt". The directory was probably tidied up after a
previous build.
To rebuild, delete, move, or rename the "minetest" directory and try
again.
""")
exit(1)
#---------------------------------------------------------------------
# Delete leftover temporary files.
tmpFileNames = [
"C.includecache",
"CXX.includecache",
"CMakeCache.txt",
"CMakeCCompiler.cmake",
"CMakeCXXCompiler.cmake",
"CMakeDirectoryInformation.cmake",
"CMakeRuleHashes.txt",
"CPackConfig.cmake",
"CPackSourceConfig.cmake",
"DependInfo.cmake",
"Makefile2",
"TargetDirectories.txt",
"build.make",
"depend.make",
"depend.internal",
"cmake_config.h",
"cmake_install.cmake",
"flags.make",
"link.txt",
"progress.make",
"relink.txt"
]
tmpFilePaths = [
"textures/base/pack/menu_header_old.png"
]
tmpExtensions = [
".a",
".log",
".o"
]
androidSub = os.path.join("build", "android")
for root, dirs, files in os.walk(".", topdown=False):
for name in files:
subPath = os.path.join(root, name)
if name in tmpFileNames:
os.remove(subPath)
elif (name == "Makefile") and (androidSub in subPath):
os.remove(subPath)
elif endsWithAny(subPath, tmpFilePaths + tmpExtensions):
os.remove(subPath)
#---------------------------------------------------------------------
# Define paths for some ".a" library files.
IRRLICHT_LIBRARY = os.path.join(LIBDIR, "libIrrlicht.a")
LEVELDB_LIBRARY = os.path.join(LIBDIR, "libleveldb.a")
LUA_LIBRARY = os.path.join(LIBDIR, "libluajit-5.1.a")
SQLITE3_LIBRARY = os.path.join(LIBDIR, "libsqlite3.a")
#---------------------------------------------------------------------
# Set "$XCFLAGS" (extra compiler flags).
XCFLAGS = "-O2 -I" + INCDIR
if MAKEDEBUG:
XCFLAGS += " -g"
if not PORTABLE:
XCFLAGS = "-march=native " + XCFLAGS
XCFLAGS += " -Wl,-L" + LIBDIR + " -Wl,-R" + LIBDIR
if os.path.isdir(LIB64DIR):
XCFLAGS += " -Wl,-L" + LIB64DIR + " -Wl,-R" + LIB64DIR
print("XCFLAGS="+XCFLAGS)
#---------------------------------------------------------------------
# Get pathnames for "gcc" and "g++" compilers.
WHICH_GCC = which("gcc")
WHICH_GPP = which("g++")
if not isExecutableFile(WHICH_GCC):
if WHICH_GCC is not None:
print("gcc: {}".format(WHICH_GCC))
customExit("gcc is not present or not executable in {}."
"".format(os.environ["PATH"]))
if not isExecutableFile(WHICH_GPP):
if WHICH_GPP is not None:
print("g++: {}".format(WHICH_GPP))
customExit("g++ is not present or not executable in {}."
"".format(os.environ["PATH"]))
#---------------------------------------------------------------------
# Handle another "--edgy step".
if Flags["edgy"]:
CM = 'src/defaultsettings.cpp'
# TODO: finish converting this from perl
print("edgy changing {} is not yet implemented."
"".format(CM))
data = None
try:
with open(CM, 'r') as IFD:
pass
#SS = $/
#undef $/;
#data = <IFD>
#data = ""unless defined + data
#/ = SS
# - secure.enable_security to false by default
except FileNotFoundError:
customExit("Internal error 0766")
# try:
# with open(CM) as OFD:
# OFD.write(data)
# except FileNotFoundError:
# customExit("Internal error 0777")
#---------------------------------------------------------------------
# Handle "--fakemt4".
if Flags["fakemt4"]:
CM = 'CMakeLists.txt'
print("fakemt4 changing {} is not yet implemented."
"".format(CM))
# TODO: handle fakemt4
# data = None
# with open(CM, 'r') as IFD: # or die "Internal error 0789\n";
# SS = $/
# undef $/;
# data = <IFD>
# data = ""unless defined + data
# / = SS
# pat = << 'END'
# set\(VERSION_MAJOR \d+\)
# .*?
# if \(CMAKE_BUILD_TYPE STREQUAL Debug\)
# END
# pat = evalSed('s@\s+\z@@s')
# pat = evalSed('s@\s*\n\s*@@gs')
# TODO: Use the segment variable here
# data = evalSed('s@\s*$pat\s*@\n$segment@is')
# with open(CM, 'w') as OFD: # or die "Internal error 0806\n";
# OFD.write(data)
# OFD.close() # or die "Internal error 0808\n";
#---------------------------------------------------------------------
# Handle "--oldproto".
if Flags["oldproto"]:
CM = 'src/network/networkprotocol.h'
print("oldproto changing {} is not yet implemented."
"".format(CM))
# TODO: change protocol in CM:
# data = None
# with open(CM, 'r') as IFD: #or die "Internal error 0714\n";
# SS = $/
# undef $/;
# data = <IFD>
# data = ""unless defined + data
# / = SS
# data = evalSed('s@(#define\s+LATEST_PROTOCOL_VERSION)\s+3\d\b@$1 32@')
# with open("CM", 'w') as OFD: # or die "Internal error 0715\n";
# OFD.write(data)
#---------------------------------------------------------------------
# Run "cmake".
cmdParts = ["cmake"]
cmdParts.append("-DCMAKE_BUILD_TYPE=release")
cmdParts.append("-DCMAKE_C_COMPILER="+WHICH_GCC)
cmdParts.append("-DCMAKE_CXX_COMPILER="+WHICH_GPP)
cmdParts.append("-DCMAKE_INSTALL_RPATH_USE_LINK_PATH=1")
cmdParts.append("-DCMAKE_SKIP_INSTALL_RPATH=0")
cmdParts.append("-DCMAKE_SKIP_RPATH=0")
cmdParts.append(client_line)
cmdParts.append(server_line)
cmdParts.append("-DENABLE_LEVELDB=1")
cmdParts.append(postgres_line)
cmdParts.append(redis_line)
cmdParts.append("-DENABLE_SOUND=1")
cmdParts.append("-DENABLE_SPATIAL=0")
# ^ TODO: WHY 0 in linux-minetest-kit?
cmdParts.append("-DENABLE_SYSTEM_JSONCPP=0")
cmdParts.append("-DRUN_IN_PLACE=1")
cmdParts.append("-DIRRLICHT_INCLUDE_DIR={}/irrlicht".format(INCDIR))
cmdParts.append("-DIRRLICHT_LIBRARY="+IRRLICHT_LIBRARY)
cmdParts.append("-DLEVELDB_INCLUDE_DIR={}/leveldb".format(INCDIR))
cmdParts.append("-DLEVELDB_LIBRARY="+LEVELDB_LIBRARY)
cmdParts.append("-DLUA_INCLUDE_DIR={}/luajit-2.1".format(INCDIR))
cmdParts.append("-DLUA_LIBRARY="+LUA_LIBRARY)
cmdParts.append("-DSQLITE3_INCLUDE_DIR="+INCDIR)
cmdParts.append("-DSQLITE3_LIBRARY="+SQLITE3_LIBRARY)
cmdParts.append("-DCMAKE_C_FLAGS=\"{}\"".format(XCFLAGS))
cmdParts.append("-DCMAKE_CXX_FLAGS=\"{}\"".format(XCFLAGS))
cmdParts.append("-DCMAKE_C_FLAGS_RELEASE=\"{}\"".format(XCFLAGS))
cmdParts.append("-DCMAKE_CXX_FLAGS_RELEASE=\"{}\"".format(XCFLAGS))
cmdParts.append(".")
print("")
print("")
print("* running cmake in {}:".format(os.getcwd()))
print(" ".join(cmdParts))
print("")
print("")
RunCmd(cmdParts[:1] + [" ".join(cmdParts[1:])], shell=False)
# ^ must be false to avoid inserting quotes automatically
# TODO: use some absolute pathnames as the Perl version does
#---------------------------------------------------------------------
# Replace some "-l..." switches with absolute pathnames.
replacements = {
"-lIrrlicht": IRRLICHT_LIBRARY,
"-lleveldb": LEVELDB_LIBRARY,
"-lluajit-5.1": LUA_LIBRARY,
"-lsqlite3": SQLITE3_LIBRARY,
}
for root, dirs, files in os.walk(PRODDIR):
for name in files:
subPath = os.path.join(root, name)
if name == "link.txt":
streamEdit(subPath, replacements)
#---------------------------------------------------------------------
# Build the program.
NUMJOBS = 3
print("* running make in {}...".format(os.getcwd()))
RunCmd(["make", "clean"])
RunCmd("make", "-j{}".format(NUMJOBS))
serverlistDir = os.path.join(PRODDIR, "client", "serverlist")
os.makedirs(serverlistDir, exist_ok=True)
os.makedirs("games", exist_ok=True)
os.makedirs("worlds", exist_ok=True)
dstAKPath = os.path.join(PRODDIR, "arrowkeys.txt")
shutil.copy(os.path.join(BALLDIR, "arrowkeys.txt"), dstAKPath)
#---------------------------------------------------------------------
# Add preloaded cache.
thisCache = os.path.join(PRODDIR, "cache")
if os.path.isdir(thisCache):
shutil.rmtree(thisCache)
tarPath = os.path.join(BALLDIR, "cachemedia.tar.bz2")
tar = tarfile.open(tarPath)
tar.extractall(path=PRODDIR)
tar.close()
#---------------------------------------------------------------------
# Add "_games".
pushd('games');
cmd = ""
gameNames = ["minimal", "amhi_game"]
if not Flags["edgy"]:
# TODO: What does Flags["edgy"] do here? Does it leave the old
# Bucket_Game and do nothing else differently?
# See mtcompile-program.pl
gameNames.append("Bucket_Game")
gamePaths = [os.path.join(PRODDIR, gName) for gName in gameNames]
print("* purging gamepaths: {}".format(gamePaths))
for gameName in gameNames:
gamePath = os.path.join(PRODDIR, gameName)
if os.path.isdir(gamePath):
shutil.rmtree(gamePath)
zipPath = os.path.Join(BALLDIR, gameName + ".zip")
if os.path.isfile(zipPath):
zf = ZipFile(zipPath)
zf.extractall(gamePath) # pwd means password in this case
popd();
#---------------------------------------------------------------------
# Add worlds.
#pushd('worlds');
WORLDS_PATH = os.path.join(PRODDIR, "worlds")
for worldName in ["Bucket_City", "Wonder_World"]:
worldPath = os.path.join(WORLDS_PATH, worldName)
if os.path.isdir(worldPath):
shutil.rmtree(worldPath)
if Flags["edgy"]:
continue
tarPath = os.path.join(BALLDIR, worldName)
if os.path.isfile(tarPath):
tar = tarfile.open(tarPath)
tar.extractall(path=WORLDS_PATH)
tar.close()
else:
print("WARNING: \"{}\" is missing.".format(tarPath))
#popd();
#---------------------------------------------------------------------
# Strip the executable(s).
if not MAKEDEBUG:
for thisBinName in ["minetest", "minetestserver"]:
thisBinPath = os.path.join(PRODDIR, "bin", thisBinName)
if not os.path.isfile(thisBinPath):
continue
RunCmd(["strip", thisBinPath])
#---------------------------------------------------------------------
# Additional cleanup.
if TIDYUP:
tmpNames = ["MakeFile", "build", "debug.txt", "lib", "src"]
tmpEndsWith = ["cmake"]
tmpStartsWith = ["CMake"]
for root, dirs, files in os.walk(".", topdown=False):
for name in files:
subPath = os.path.join(root, name)
if name in tmpNames:
if os.path.isfile(subPath):
os.remove(subPath)
else:
shutil.rmtree(subPath)
elif endsWithAny(subPath, tmpEndsWith):
if os.path.isfile(subPath):
os.remove(subPath)
else:
shutil.rmtree(subPath)
elif startsWithAny(subPath, tmpStartsWith):
if os.path.isfile(subPath):
os.remove(subPath)
else:
shutil.rmtree(subPath)
keepWith = [" ", ".dummy"]
for root, dirs, files in os.walk(".", topdown=False):
for name in files:
subPath = os.path.join(root, name)
if containsAny(name, keepWith):
continue
if not name.startswith("."):
continue
if name[1:2].upper() != name[1:2]:
# .[a-z]\*
os.remove(subPath)
#---------------------------------------------------------------------
# Finish required "--portable" operations.
if PORTABLE:
M = os.uname().machine
BITS = 32 if (M == "i686") else 64
solibPath = os.path.join(PRODDIR, "solib")
if os.path.isdir(solibPath):
shutil.rmtree(solibPath)
tarPath = os.path.join(BALLDIR, "solib{}.tar.bz2".format(BITS))
if os.path.isfile(tarPath):
tar = tarfile.open(tarPath)
tar.extractall(path=PRODDIR)
if not os.path.isdir(solibPath):
customExit("ERROR: extracting \"{}\" did not result in"
" \"{}\"".format(tarPath, solibPath))
tar.close()
else:
customExit("ERROR: \"{}\" is missing.".format(tarPath))
# pushd('bin');
for prog in ["minetest", "minetestserver"]:
srcWrapPath = os.path.join(BALLDIR, prog+".wrapper")
progWrapPath = os.path.join(PRODDIR, "bin", prog)
progBinPath = os.path.join(PRODDIR, "bin", prog+".bin")
if os.path.isfile(progBinPath):
os.remove(progBinPath)
shutil.move(progWrapPath, progBinPath)
# copy2 mimics `cp -p` (preserve attributes)
shutil.copy2(srcWrapPath, progWrapPath)
os.chmod(progWrapPath, 0o755)
# popd()
#---------------------------------------------------------------------
if MAKEPROD:
# os.chdir(THISDIR) # or die "$IE #505833\n";
ZIPDIR = "minetest-linux" + BITS
ZIPDIR_PATH = os.path.join(THISDIR, ZIPDIR)
ZIPFILE = ZIPDIR + ".zip"
ZIPFILE_PATH = os.path.join(THISDIR, ZIPFILE)
if os.path.isdir(ZIPDIR_PATH):
shutil.rmtree(ZIPDIR_PATH)
if os.path.isfile(ZIPFILE_PATH):
os.remove(ZIPFILE_PATH)
shutil.move(PRODDIR_PATH, ZIPDIR_PATH)
zf = zipfile.ZipFile(ZIPFILE_PATH, 'w',
zipfile.ZIP_DEFLATED, compresslevel=9)
zipdir(ZIP_PATH, zf)
zf.close()
# originally zip -ro9q
# r: recurse into subdirectories (done by zipdir)
# o: make zipfile as old as latest entry (handled below)
# 9: compress better
# q: quiet
latestStamp = None
for root, dirs, files in os.walk(ZIPDIR_PATH):
for name in files:
subPath = os.path.join(root, name)
stamp = os.stat("ideas.md").st_mtime
if (latestStamp is None) or (stamp > latestStamp):
latestStamp = stamp
st = os.stat(ZIPFILE_PATH)
# See <https://www.gubatron.com/blog/2007/05/29/how-to-
# update-file-timestamps-in-python/>
atime = st[stat.ST_ATIME]
mtime = st[stat.ST_MTIME]
shutil.rmtree(ZIPDIR_PATH)
os.utime(ZIPFILE_PATH, (atime, latestStamp))
print("Done\n")
# end main
if __name__ == "__main__":
main()