# vim:se syn=tcl:
#

use cc cc-lib cc-shared pkg-config

set maj_ver 1
set med_ver 17
set min_ver 5
set dev_ver 0
define PKG_API [expr $maj_ver * 1000000 + $med_ver * 1000 + $min_ver]
define VERSION $maj_ver.$med_ver.$min_ver[expr {$dev_ver ? ".$dev_ver" : ""}]

# Add any user options here
options {
	pkgconfigdir:WHERE => "path to the directory where to install pc files"
	with-ldns => "add support for libldns"
	with-libarchive.pc => "build with libarchive getting flags via pc files"
	with-coverage => "build with llvm coverage support"
	with-asan => "build with libasan support"
	with-ubsan => "Build with libubsan support"
	default-format:txz => "Default compression format: tzst, txz (default), tbz, tar"
}

if {[opt-str pkgconfigdir dir]} {
	define pkgconfigdir $dir
} else {
	if {[string match *-freebsd* [get-define host]]} {
		define pkgconfigdir [get-define prefix]/libdata/pkgconfig
	} else {
		define pkgconfigdir [get-define prefix]/lib/pkgconfig
	}
}

# default compression format

if {[opt-str default-format fmt]} {
	switch $fmt \
	    "tzst" { set comp TZS } \
	    "txz" { set comp TXZ } \
	    "tbz" { set comp TBZ } \
	    "tgz" { set comp TGZ } \
	    "tar" { set comp TAR } \
	    "default" { user-error "Invalid format $fmt" }
	define DEFAULT_COMPRESSION $comp
} else {
	define DEFAULT_COMPRESSION TXZ
}

cc-check-tools ar ranlib strip

define EXTRA_LIBS ""
define LIBVERSION 4
define LIBSOEXT [format [get-define SH_SOEXTVER] [get-define LIBVERSION]]

define GITHASH ""
if {[cc-check-progs git] && [file exists .git] &&
    ![catch {exec git rev-parse --short HEAD} gitrev] &&
    ![catch {exec git diff-index -m --name-only HEAD} gitdirty]} {
	define GITHASH -$gitrev[expr {$gitdirty eq {} ? {} : {-dirty}}]
}

if {[string match *-linux* [get-define host]]} {
	cc-with { -libs { -lbsd }} {
		if {[cc-check-functions getprogname]} {
			define-append EXTRA_LIBS -lbsd
			define-feature libbsd
		} else {
			user-error "Unable to find libbsd"
		}
	}
} else {
	define-feature static
}

if {![opt-bool with-libarchive.pc]} {
	cc-with { -libs { -larchive }} {
		if {![cc-check-functions archive_read_open]} {
			user-error "Unable to find libarchive"
		}
	}
}

cc-with { -libs { -lz }} {
	if {![cc-check-functions zlibVersion]} {
		user-error "Unable to find zlib"
	}
}

cc-with { -libs { -lbz2 }} {
	if {![cc-check-functions BZ2_bzReadOpen]} {
		user-error "Unable to find bzip2"
	}
}

cc-with { -libs { -llzma }} {
	if {![cc-check-functions lzma_version_string]} {
		user-error "Unable to find liblzma"
	}
}
if {[string match *-freebsd* [get-define host]]} {
	cc-with { -libs { -lmd }} {
		if {![cc-check-functions SHA256_Data]} {
			user-error "Unable to find libmd"
		} else {
			define-feature libmd
		}
	}
}

cc-with { -libs { -larchive }} {
	cc-check-functions archive_write_add_filter_zstd
}

define ZSTDLIB ""
cc-with { -libs { -lprivate_zstd }} {
	if {[cc-check-functions ZSTD_versionNumber]} {
		define ZSTDLIB "-lprivate_zstd"
	}
}

# Atomics
msg-checking "Checking for atomic builtins... "
if {[cctest -code {
	volatile unsigned long val = 1;
	__sync_synchronize();
	__sync_val_compare_and_swap(&val, 1, 0);
	__sync_add_and_fetch(&val, 1);
	__sync_sub_and_fetch(&val, 1);
	return 0;
	}
]} {
	msg-result ok
	define-feature atomic_builtins
} else {
	msg-result no
	define-feature atomic_builtins 0
}

msg-checking "Checking for /proc/self/fd support... "
if {[file exists /proc/self/fd]} {
	msg-result ok
	define-feature proc-pid
} else {
	msg-result no
	define-feature proc-pid 0
}

foreach fct [list memmove usleep pread pwrite] {
	if {![cc-check-functions $fct]} {
		user-error "Cannot find $fct"
	}
}

cc-with { -includes fcntl.h } {
	cc-check-decls F_CLOSEM
}

cc-with { -includes sys/socket.h } {
	cc-check-decls SOCK_SEQPACKET
}

cc-with { -includes netinet/in.h } {
	cc-check-members "struct sockaddr_in.sin_len"
}

cc-with { -includes sys/stat.h } {
	cc-check-members "struct stat.st_mtim" "struct stat.st_flags"
}

# check for sqlite
cc-check-functions gmtime_r isnan localtime_r strchrnul strerror_r fdatasync \
	fchown lstat readlink
cc-check-includes stdint.h inttypes.h

# check for pkg itself
cc-check-functions arc4random arc4random_stir basename_r chflagsat \
	closefrom dirfd eaccess fopencookie fstatfs funopen getprogname strnstr \
	strtofflags strtonum sysconf utimensat __res_setservers unlinkat \
	faccessat fstatat openat readlinkat fflagstostr reallocarray strchrnul

cc-with { -includes { libutil.h }} {
	if {[cc-check-decls humanize_number]} {
	}
}

if {![cc-check-functions fts_open]} {
	cc-with { -libs { -lfts }} {
		if {[cc-check-functions fts_open]} {
			define-feature LIBFTS
		}
	}
}

if {[get-define HAVE_DECL_HUMANIZE_NUMBER] eq 1} {
	cc-with { -libs { -lutil }} {
		if {[cc-check-functions humanize_number]} {
			define-feature LIBUTIL
		}
	}
}

if {![cc-check-functions dlclose]} {
	cc-with { -libs { -ldl }} {
		if {[cc-check-functions dlclose]} {
			define-feature LIBDL
		}
	}
}

if {![cc-check-functions __res_query]} {
	cc-with { -libs { -lresolv }} {
		if {[cc-check-functions __res_query]} {
			define-feature LIBRESOLV
		}
	}
}

cc-check-includes link.h machine/endian.h osreldate.h readpassphrase.h \
	sys/procctl.h sys/statfs.h sys/statvfs.h libutil.h

# for compat
cc-check-includes dirent.h

#endian stuff
foreach header [list endian.h sys/endian.h] {
	if {[cc-check-includes $header]} {
		cc-with [list -includes $header] {
			cc-check-decls be16dec be16enc be32dec be32enc be64dec be64enc \
				le16dec le16enc le32dec le32enc le64dec le64enc
		}
	}
}

if {[string match *-darwin* [get-define host]]} {
	define libmachista
	define waflags ""
	define nowaflags ""
} else {
# libelf
	define waflags "-Wl,-whole-archive"
	define nowaflags "-Wl,-no-whole-archive"
	cc-with { -includes { sys/types.h }} {
		cc-check-includes elf-hints.h sys/elf_common.h
	}

	if {[cc-check-includes gelf.h libelf.h]} {
		cc-with { -libs { -lelf } } {
			if {[cc-check-functions gelf_getehdr]} {
				define-feature libelf
				define libelf-external
				define-append EXTRA_LIBS -lelf
				cc-check-types Elf_Note
			} else {
				define libelf-internal
				define-feature libelf 0
			}
		}
	} else {
		define-feature libelf 0
		define libelf-internal
	}
}

cc-with { -libs { -ljail} } {
	if {[cc-check-functions jail_getid]} {
		define-feature libjail
		define-append EXTRA_LIBS -ljail
	}
}

# libbsd
cc-check-includes bsd/err.h bsd/libutil.h bsd/readpassphrase.h \
	bsd/stdio.h bsd/stdlib.h bsd/strlib.h bsd/string.h \
	bsd/sys/cdefs.h bsd/unistd.h
 
if {[opt-bool with-asan]} {
	define-append ASAN_CFLAGS -O0 -ggdb -fsanitize=address
	define-append ASAN_LDFLAGS -fsanitize=address
	if {[string match *-linux* [get-define host]]} {
		# ASAN depends on libdl for dlsym when building with GCC
		define-append ASAN_LDFLAGS -ldl
	}
	define asan 1
	undefine HAVE_STATIC
}
if {[opt-bool with-ubsan]} {
	# Avoid alignment error when building with UBSan inside _mum_hash_avx2
	define-append UBSAN_CFLAGS -D_MUM_UNALIGNED_ACCESS=0
	define-append UBSAN_CFLAGS -O0 -ggdb -fsanitize=undefined
	define-append UBSAN_LDFLAGS -fsanitize=undefined
	define ubsan 1
}
if {[opt-bool with-coverage]} {
	define-append COVERAGE_CFLAGS -O0 -ggdb -fprofile-instr-generate -fcoverage-mapping
	define-append COVERAGE_LDFLAGS -fprofile-instr-generate -fuse-ld=/usr/local/bin/ld.lld
	define CC /usr/local/bin/clang
	define CC_FOR_BUILD /usr/local/bin/clang
	define coverage 1
# disable capsicum when coverage is used because coverage does not work in
# capsicum mode
} else {
# capsicum 
	if {[cc-check-functions cap_sandboxed]} {
		define-feature capsicum
			cc-check-includes sys/capsicum.h
	}
}

if {[pkg-config-init 0]} {
# atf
	if {[pkg-config atf-c] && [cc-check-progs kyua]} {
		define TESTS
		puts "test suite will be built"
	} else {
		puts "Skipping test suite"
	}
	if {[opt-bool with-ldns]} {
		if {![pkg-config libldns]} {
			user-error "Unable to find libldns"
		} else {
			define PKG_LIBDNS_LIBS_STATIC [exec pkg-config --static --libs-only-l libldns]
		}
	}
	if {[opt-bool with-libarchive.pc]} {
		if {![pkg-config libarchive]} {
			user-error "Unable to find libarchive"
		} else {
			define PKG_LIBARCHIVE_LIBS_STATIC [exec pkg-config --static --libs-only-l libarchive]
			cc-with { -libs { -larchive }} {
				cc-check-functions archive_write_add_filter_zstd
			}
		}
	}
}

foreach repo [list binary] {
	define-append REPOS $repo
	define-append REPOS_LDFLAGS -L\$(top_builddir)/libpkg/repo/${repo} -lrepo-${repo}_pic
	define-append REPOS_STATIC_LIBS \$(top_builddir)/libpkg/repo/${repo}/librepo-${repo}.a
}

# ccache
if {[get-define CCACHE] ne {none}} {
  define CC "[get-define CCACHE] [get-define CC]"
}

make-config-header pkg_config.h -bare { DEFAULT_* }
make-config-header external/sqlite/config.h
make-template mk/defs.mk.in
make-template libpkg/pkg.h.in
make-template libpkg/pkg.pc.in
make-template tests/frontend/test_environment.sh.in
make-template Makefile.autosetup Makefile

foreach dir [list external/blake2 external/picosat \
	external/linenoise external/libfetch external/sqlite \
	external compat libpkg libpkg/repo libpkg/repo/binary src \
	external/libucl external/libelf external/libmachista tests docs \
	external/liblua external/msgpuck external/yxml scripts] {
	make-template $dir/Makefile.autosetup $dir/Makefile
}

