#! /usr/bin/env python
# encoding: utf-8
# Thomas Nagy, 2006 (ita)

"""
Batched builds
instead of compiling object files one by one, c/c++ compilers are often able to compile at once:
cc -c ../file1.c ../file2.c ../file3.c

Files are output on the directory where the compiler is called, and dependencies are more difficult
to track (do not run the command on all source files if only one file changes)

As such, we do as if the files were compiled one by one, but no command is actually run:
replace each cc/cpp Task by a TaskSlave
A new task called TaskMaster collects the signatures from each slave and finds out the command-line
to run.

To set this up, the method ccroot::create_task is replaced by a new version, to enable batched builds
it is only necessary to import this module in the configuration (no other change required)
"""

import shutil
import Action, Object, Task, ccroot

class TaskMaster(Task.Task):
	def __init__(self, action_name, env, priority=5, normal=1, master=None):
		Task.Task.__init__(self, action_name, env, priority, normal)
		self.slaves=[]
		self.m_inputs2=[]
		self.m_outputs2=[]

	def add_slave(self, slave):
		self.slaves.append(slave)
		self.m_run_after.append(slave)

	def may_start(self):
		for t in self.m_run_after:
			if not t.m_hasrun: return 0

		for t in self.slaves:
			self.m_inputs.append(t.m_inputs[0])
			self.m_outputs.append(t.m_outputs[0])
			if t.m_must_run:
				self.m_inputs2.append(t.m_inputs[0])
				self.m_outputs2.append(t.m_outputs[0])
		return 1

	def run(self):
		tmpinputs = self.m_inputs
		self.m_inputs = self.m_inputs2
		tmpoutputs = self.m_outputs
		self.m_outputs = self.m_outputs2

		ret = self.m_action.run(self)

		# unfortunately building the files in batch mode outputs them in the current folder (the build dir)
		# now move the files from the top of the builddir to the correct location
		for i in self.m_outputs:
			name = i.m_name
			if name[-1] == "s": name = name[:-1] # extension for shlib is .os, remove the s
			shutil.move(name, i.bldpath(self.m_env))

		self.m_inputs = tmpinputs
		self.m_outputs = tmpoutputs

		return ret

class TaskSlave(Task.Task):
	def __init__(self, action_name, env, priority=5, normal=1, master=None):
		Task.Task.__init__(self, action_name, env, priority, normal)
		self.m_master = master

	def get_display(self):
		self.m_display=""
		return ""

	def update_stat(self):
		self.m_executed=1

	def must_run(self):
		self.m_must_run = Task.Task.must_run(self)
		return self.m_must_run

	def run(self):
		return 0

	def can_retrieve_cache(self, sig):
		return None

def create_task_new(self, type, env=None, nice=10):
	if type == "cc" or type == "cpp":

		if env is None: env=self.env
		try:
			mm = self.mastertask
		except AttributeError:
			mm = TaskMaster("all_"+type, env, nice)
			self.mastertask = mm

		task = TaskSlave(type, env, nice, master=mm)
		self.m_tasks.append(task)

		mm.add_slave(task)

		if type == self.m_type_initials:
			self.p_compiletasks.append(task)
		return task

	task = Object.genobj.create_task(self, type, env, nice)
	if type == self.m_type_initials:
		self.p_compiletasks.append(task)
	return task

def detect(conf):
	return 1

def setup(env):
	cc_str = '${CC} ${CCFLAGS} ${CPPFLAGS} ${_CCINCFLAGS} ${_CCDEFFLAGS} -c ${SRC}'
        Action.simple_action('all_cc', cc_str, 'GREEN')

	cpp_str = '${CXX} ${CXXFLAGS} ${CPPFLAGS} ${_CXXINCFLAGS} ${_CXXDEFFLAGS} -c ${SRC}'
	Action.simple_action('all_cpp', cpp_str, color='GREEN')

	ccroot.ccroot.create_task = create_task_new

