Clone in a terminal:
#! /usr/bin/env python

import os, sys, imp, time
from waflib import Context, Options, Configure, Utils, Logs, TaskGen, Task, Build, ConfigSet
import waflib.Tools.c

"""
Create a modified waf file in which tasks use timestamps only
see README.txt
"""

# we hard-code a configuration for c but it could be left in the script file too
def configure(conf):
	conf.load('gcc')

def recurse_rep(x, y):
	f = getattr(Context.g_module, x.cmd or x.fun, Utils.nada)
	return f(x)

def start(cwd, version, wafdir):
	# this is the entry point of our small build system

	Logs.init_log()
	Context.waf_dir = wafdir
	Context.out_dir = Context.top_dir = Context.run_dir = cwd
	Context.g_module = Context.load_module(cwd + os.sep + 'wscript')
	Context.g_module.configure = configure
	Context.g_module.root_path = cwd
	Context.Context.recurse = recurse_rep

	Context.g_module.top = Context.g_module.out = '.' # no build directory

	# just parse the options and execute a build
	Options.OptionsContext().execute()

	conf = Context.create_context('configure')
	conf.options = Options.options
	conf.execute()

	bld = Context.create_context('build')
	bld.env = conf.env
	bld.options = Options.options
	bld.environ = os.environ
	bld.execute()

# change the build context so it does not need to write any file
class StatelessBuild(Build.BuildContext):
	def load_envs(self):
		self.env = ConfigSet.ConfigSet()
	def store(self):
		pass
	def restore(self):
		self.init_dirs()
	def execute_build(self):
		# we override this method to hide the messages "leaving directory" (just because)
		self.recurse([self.run_dir])
		self.pre_build()

		self.timer = Utils.Timer()

		if Options.options.progress_bar:
			sys.stderr.write(Logs.colors.cursor_off)
		try:
			self.compile()
		finally:
			if Options.options.progress_bar:
				sys.stderr.write(Logs.colors.cursor_on)
				print('')
		self.post_build()

class SilentConf(Configure.ConfigurationContext):
	# silent configuration
	def __init__(self, **kw):
		# disable the configuration messages from Context.start_msg/end_msg
		self.in_msg = 1
		super(SilentConf, self).__init__(**kw)

	def execute(self):

		# copy-paste from the original method, but without the cache file creation
		self.init_dirs()

		path = os.path.join(self.bldnode.abspath(), 'config.log')
		self.logger = Logs.make_logger(path, 'cfg')

		app = getattr(Context.g_module, 'APPNAME', '')
		if app:
			ver = getattr(Context.g_module, 'VERSION', '')
			if ver:
				app = "%s (%s)" % (app, ver)

		now = time.ctime()
		pyver = sys.hexversion
		systype = sys.platform
		args = " ".join(sys.argv)
		wafver = Context.WAFVERSION
		abi = Context.ABI
		self.to_log(Configure.conf_template % vars())

		super(Configure.ConfigurationContext, self).execute()


# change the superclass of existing tasks to force timestamps (the build has no state)
def status(self):
	for t in self.run_after:
		if not t.hasrun:
			return Task.ASK_LATER

	implicit_deps = []
	try:
		implicit_deps, _ = self.scan()
	except:
		pass

	# we can add one more node, for example:
	implicit_deps.append(self.generator.path.make_node('wscript'))

	for x in self.inputs + self.dep_nodes + implicit_deps:
		for y in self.outputs:
			try:
				if os.stat(x.abspath()).st_mtime > os.stat(y.abspath()).st_mtime:
					return Task.RUN_ME
			except:
				return Task.RUN_ME

	return Task.SKIP_ME
Task.Task.runnable_status = status

# the post build execution does not need to deal with signatures or anything else
Task.Task.post_run = Utils.nada

Contact

Email: johan.mattsson.m@gmail.com
Mastodon: typo.social/@birdfont
GitHub: johanmattssonm
LinkedIn: johan-mattsson-2ab324112