Import('env')

AddOption('--no-libprocps', dest="no_libprocps", action="store_true", default=False, help="do not make use of libprocps, daemon will not be able to do process/thread listings")
AddOption('--pkg-config', dest="PKG_CONFIG", type="string", metavar="EXECUTABLE", help="pkg-config executable to use. default is to search path for `pkg-config`")

if GetOption("help"):
    # early exit here not to execute config checks
    Return()

for opt in "PKG_CONFIG".split(","):
    env[opt] = GetOption(opt)

env = env.Clone()
test_env = env.Clone()

common_compiler_flags = [ "-Wall" ]
env.Prepend(
    CPPPATH=[ "./src" ],
)
plat_libs = []
if not GetOption("clean") and "__LINUX__" in env["CPPDEFINES"]:
    plat_libs.extend(["dl", "util"])

env.Append(
    LIBS=[ "string_util" ] + plat_libs,
    CFLAGS=common_compiler_flags,
    CXXFLAGS=common_compiler_flags,
)

if GetOption("clean") or "__QNX__" in env["CPPDEFINES"]:
    no_libprocps = True
else:
    no_libprocps = GetOption("no_libprocps")

if no_libprocps:
    print("Building without libprocps!")
    procps_defines = [ '-DNO_LIBPROCPS' ]
else:
    procps_defines = []

conf_env = env.CloneConfigureEnv()
conf = conf_env.Configure() # todo: can write config_h on its own
if not no_libprocps:
    if env["PKG_CONFIG"] is None:
        env["PKG_CONFIG"] = env.popen(['which', 'pkg-config']).strip()
        if not env["PKG_CONFIG"]:
            print("Error: could not find pkg-config executable!")
        else:
            print("using pkg-config: %s" % env["PKG_CONFIG"])

    if conf.CheckCHeader("libproc2/pids.h"):
        procps_lib = "proc2"
        procps_defines.append("-DLIBPROC2")
        pids_val_with_info_code = r"""
        #include <libproc2/pids.h>
        int main(void) {
            struct pids_stack* stack = 0;
            struct pids_info* info = 0;
            return PIDS_VAL(0, s_int, stack, info);
        }
        """
        pids_val_takes_info = conf.TryCompile(pids_val_with_info_code, extension=".c")
        print("libproc2 PIDS_VAL takes info argument: %s" % ("yes" if pids_val_takes_info else "no"))
        if pids_val_takes_info:
            procps_defines.append("-DLIBPROC2_PIDS_VAL_TAKES_INFO")
    else:
        if not conf.CheckCHeader("proc/readproc.h"):
            print("please install libproc2-dev (or libprocps-dev)\nor use the --no-libprocps option to disable process/thread listings")
            Exit(1)
        procps_lib = "procps"

        libprocps_systemd_version = 209
        import sys
        sys.stdout.write("Checking for systemd version >= %s..." % libprocps_systemd_version)
        if not env["PKG_CONFIG"]:
            print("no pkg-config! assuming your systemd version is >= %d and setting WITH_SYSTEMD for libprocps" % libprocps_systemd_version)
            ret = 0
        else:
            out, err, ret = env.popen('$PKG_CONFIG --exists  "systemd >= %s"' % libprocps_systemd_version, with_returncode=True)
        if ret == 0:
            print("yes, setting WITH_SYSTEMD for libprocps headers!")
            procps_defines.append("-DWITH_SYSTEMD=1")
        else:
            print("no")

        if conf.CheckFunc("open_psdb", header="#include <proc/readproc.h>", language="C"):
            print("you have an older version of libprocps installed. will try to compensate for that...")
            procps_defines.append('-DOLD_LIBPROCPS')
    env.Append(LIBS=[ procps_lib ])

if GetOption("no_daemon_auth"):
    env.Append(CPPDEFINES='-DNO_DAEMON_AUTH')

test_code = r"""
#include <unistd.h>
int main(void) {
    return nice(0);
}
"""
test_code2 = r"""
#include <sys/time.h>
#include <sys/resource.h>

int main(void) {
    return setpriority(PRIO_PROCESS, 0, 0);
}
"""
if conf.TryLink(test_code, extension=".c"):
    env.Append(CPPDEFINES=["HAVE_NICE"])
elif conf.TryLink(test_code2, extension=".c"):
    env.Append(CPPDEFINES=["HAVE_SETPRIORITY"])

if not GetOption("clean") and not conf.CheckCXXHeader("string_util/string_util.h"):
    print("please install libstring_util e.g. from https://gitlab.com/links_and_nodes/libstring_util")
    Exit(1)
conf.Finish()

env.Append(CPPDEFINES=procps_defines)

sources = Glob("src/*.c*")

if not GetOption("clean"):
    env.write_version_h("src/version.h", "LN_RUNTIME_VERSION", sources=sources)
    env.write_config_h("src/config.h", sources=sources)

env.Append(LINKFLAGS=["-Wl,-rpath,'$$ORIGIN/../../libln'"])
ln_daemon = env.Program("ln_daemon", source=sources)
env.InstallProgram("$PREFIX/bin", ln_daemon)

## tests
if not no_libprocps:
    test_env.Replace(
        LIBS=[ "string_util", procps_lib, "dl", "util" ],
        OBJPREFIX="test-",
    )
    test_compiler_flags = common_compiler_flags + [ "-O0" ]
    test_env.Append(
        CPPDEFINES=["TEST"],
        CFLAGS=test_compiler_flags,
        CXXFLAGS=test_compiler_flags,
    )
    test_env.Append(CPPDEFINES=procps_defines)
    test_env.Program("test_linux_process_listing", source=[
        "src/tests/test_linux_process_listing.cpp",
        "src/filter.cpp",
        "src/linux_process_listing.cpp",
    ])
