#! /usr/bin/env waf
# encoding: utf-8
# copyright 2009 Johan Boule <bohan@jabber.org>
#
# This test builds various combinations of static and shared modules.
# The program 'main' uses the library 'wrapper', which in turn uses the library 'impl'.
# We build the following "variants" (not variants in the waf-sense):
# - a dynamically linked 'main' program that dynamically links against a shared 'wrapper' library, which dynamically links against a shared 'impl' library,
# - a dynamically linked 'main' program that contains a static 'wrapper' library, which dynamically links against a shared 'impl' library,
# - a dynamically linked 'main' program that contains a static 'wrapper' library, which depends on a static 'impl' library,
# - a statically linked 'main' program that contains a static 'wrapper' library, which depends on a static 'impl' library,
# The case of a shared 'wrapper' library that contains a static 'impl' library is not tested, as it's rarely used and needs pic/non-pic trickery.

import os, Options, Build

APPNAME = 'waf-static-shared'
VERSION = '1.2.3'

srcdir = '.'
blddir = '++build'

def init(): pass

def set_options(opt):
	opt.tool_options('compiler_cxx')

def configure(conf):
	conf.check_tool('compiler_cxx')
	conf.env.PREFIX = os.path.join(conf.blddir, os.pardir, '++install')
	if conf.env['CXX_NAME'] == 'gcc':
		conf.env.append_value('LINKFLAGS', '-Wl,--as-needed')
		#conf.env.append_value('LINKFLAGS', '-Wl,-rpath-link,' + conf.env.PREFIX + '/lib')
	
def build(bld):
	test_name = 'static-shared' #bld.srcnode.name
	src_dir = 'src'
	
	def variant(static_prog, static_wrapper, static_impl):
		variant_name = \
			(static_prog and 'st' or 'sh') + '-' + \
			(static_wrapper and 'st' or 'sh') + '-' + \
			(static_impl and 'st' or 'sh')
		impl = bld.lib(
			target=test_name + '--' + variant_name + '--impl',
			types=(static_impl and 'static' or 'shared',),
			sources=[src_dir + '/impl'],
			includes=[src_dir],
			defines=['IMPL=' + (static_impl and '-1' or '1')]
		)
		wrapper = bld.lib(
			target=test_name + '--' + variant_name + '--wrapper',
			types=(static_wrapper and 'static' or 'shared',),
			sources=[src_dir + '/wrapper'],
			includes=[src_dir],
			defines=['WRAPPER=' + (static_wrapper and '-1' or '1')],
			uselib_local=[impl.name]
		)
		bld.program(
			target=test_name + '--' + variant_name + '--main',
			types=(static_prog and 'static' or 'shared',),
			sources=[src_dir + '/main'],
			includes=[src_dir],
			uselib_local=[wrapper.name]
		)
	variant(True, True, True)
	variant(False, True, True)
	variant(False, True, False)
	variant(False, False, False)
	#variant(False, False, True) ... rarely used: a shared library that contains a static one needs pic/non-pic trickery.
	
def shutdown(): pass

def program(bld, target, types = ('static', 'shared',), sources = None, includes = None, defines = None, uselib_local = None, uselib = None):
	return module(bld, target, 'program', types, sources, includes, None, defines, uselib_local, uselib)
Build.BuildContext.program = program

def lib(bld, target, types = ('static', 'shared'), sources = None, includes = None, headers = None, defines = None, uselib_local = None, uselib = None):
	return module(bld, target, 'lib', types, sources, includes, headers, defines, uselib_local, uselib)
Build.BuildContext.lib = lib

def module(bld, target, kind, types = ('static', 'shared'), sources = None, includes = None, headers = None, defines = None, uselib_local = None, uselib = None):
	for type in types:
		if kind == 'program':
			obj = bld.new_task_gen('cxx', 'program')
			obj.target = target
			if len(types) > 1: obj.target += '-' + type
			if type == 'static' and obj.env['CXX_NAME'] == 'gcc':
				#obj.env = obj.env.copy()
				obj.env.append_value('LINKFLAGS', '-static')
		else:
			obj = bld.new_task_gen('cxx', {'static': 'staticlib', 'shared': 'shlib'}[type])
			obj.target = target
			if type == 'shared': obj.vnum = VERSION
		obj.name = target + '-' + type
		obj.source = []
		for s in sources:
			node = bld.path.find_dir(s) # is it a dir?
			if node:
				for f in node.find_iter(in_pat = ['*.cpp']): obj.source.append(f.relpath_gen(bld.path))
			else: obj.source.append(s)
		obj.includes = includes
		if defines is not None: obj.defines = defines
		if type != 'static' and obj.env.DEST_BINFMT == 'elf': obj.rpath = '$ORIGIN:$ORIGIN/../lib'
		if uselib_local: obj.uselib_local = uselib_local
		if uselib: obj.uselib = uselib
	if kind != 'program':
		if headers is None:
			headers = []
			# we search headers in source dirs
			for s in sources:
				node = bld.path.find_dir(s) # is it a dir?
				if not node:
					node = bld.path.find_resource(s) # is it a file?
					if node: node = node.parent
				if not node: print >> std.err, 'not found:', s
				if node:
					node.parent # we add headers from where the source file located
					headers.append(node.relpath_gen(bld.path))
		install_headers(bld, includes, headers)
	if len(types) == 1: return obj
Build.BuildContext.module = module

def install_headers(bld, includes, headers):
	headers_filenames = []
	for h in headers:
		#print 'header search:', h
		node = bld.path.find_dir(h) # is it a dir ?
		if node:
			for pat in '*.hpp', '*.h':
				for f in node.find_iter(in_pat = ['*.hpp']): headers_filenames.append(f.relpath_gen(bld.path))
		else: headers_filenames.append(h)
	#print 'found header files:', headers_filenames
	for i in includes:
		for h in headers_filenames:
			if h.startswith(i):
				bld.install_as('${PREFIX}/include/' + APPNAME + '-' + VERSION + '/' + h[(len(i)):], h)
Build.BuildContext.install_headers = install_headers
