Exemple #1
0
    def load_marlin_features():
        if 'MARLIN_FEATURES' in env:
            return

        # Process defines
        from preprocessor import run_preprocessor
        define_list = run_preprocessor(env)
        marlin_features = {}
        for define in define_list:
            feature = define[8:].strip().decode().split(' ')
            feature, definition = feature[0], ' '.join(feature[1:])
            marlin_features[feature] = definition
        env['MARLIN_FEATURES'] = marlin_features
Exemple #2
0
def compute_build_signature(env):
    if 'BUILD_SIGNATURE' in env:
        return

    # Definitions from these files will be kept
    files_to_keep = ['Marlin/Configuration.h', 'Marlin/Configuration_adv.h']

    build_path = Path(env['PROJECT_BUILD_DIR'], env['PIOENV'])

    # Check if we can skip processing
    hashes = ''
    for header in files_to_keep:
        hashes += get_file_sha256sum(header)[0:10]

    marlin_json = build_path / 'marlin_config.json'
    marlin_zip = build_path / 'mc.zip'

    # Read existing config file
    try:
        with marlin_json.open() as infile:
            conf = json.load(infile)
            if conf['__INITIAL_HASH'] == hashes:
                # Same configuration, skip recomputing the building signature
                compress_file(marlin_json, marlin_zip)
                return
    except:
        pass

    # Get enabled config options based on preprocessor
    from preprocessor import run_preprocessor
    complete_cfg = run_preprocessor(env)

    # Dumb #define extraction from the configuration files
    conf_defines = {}
    all_defines = []
    for header in files_to_keep:
        defines = extract_defines(header)
        # To filter only the define we want
        all_defines += defines
        # To remember from which file it cames from
        conf_defines[header.split('/')[-1]] = defines

    r = re.compile(r"\(+(\s*-*\s*_.*)\)+")

    # First step is to collect all valid macros
    defines = {}
    for line in complete_cfg:

        # Split the define from the value
        key_val = line[8:].strip().decode().split(' ')
        key, value = key_val[0], ' '.join(key_val[1:])

        # Ignore values starting with two underscore, since it's low level
        if len(key) > 2 and key[0:2] == "__":
            continue
        # Ignore values containing a parenthesis (likely a function macro)
        if '(' in key and ')' in key:
            continue

        # Then filter dumb values
        if r.match(value):
            continue

        defines[key] = value if len(value) else ""

    #
    # Continue to gather data for CONFIGURATION_EMBEDDING or CONFIG_EXPORT
    #
    if not ('CONFIGURATION_EMBEDDING' in defines
            or 'CONFIG_EXPORT' in defines):
        return

    # Second step is to filter useless macro
    resolved_defines = {}
    for key in defines:
        # Remove all boards now
        if key.startswith("BOARD_") and key != "BOARD_INFO_NAME":
            continue
        # Remove all keys ending by "_NAME" as it does not make a difference to the configuration
        if key.endswith("_NAME") and key != "CUSTOM_MACHINE_NAME":
            continue
        # Remove all keys ending by "_T_DECLARED" as it's a copy of extraneous system stuff
        if key.endswith("_T_DECLARED"):
            continue
        # Remove keys that are not in the #define list in the Configuration list
        if key not in all_defines + [
                'DETAILED_BUILD_VERSION', 'STRING_DISTRIBUTION_DATE'
        ]:
            continue

        # Don't be that smart guy here
        resolved_defines[key] = defines[key]

    # Generate a build signature now
    # We are making an object that's a bit more complex than a basic dictionary here
    data = {}
    data['__INITIAL_HASH'] = hashes
    # First create a key for each header here
    for header in conf_defines:
        data[header] = {}

    # Then populate the object where each key is going to (that's a O(N^2) algorithm here...)
    for key in resolved_defines:
        for header in conf_defines:
            if key in conf_defines[header]:
                data[header][key] = resolved_defines[key]

    # Every python needs this toy
    def tryint(key):
        try:
            return int(defines[key])
        except:
            return 0

    config_dump = tryint('CONFIG_EXPORT')

    #
    # Produce an INI file if CONFIG_EXPORT == 2
    #
    if config_dump == 2:
        print("Generating config.ini ...")
        config_ini = build_path / 'config.ini'
        with config_ini.open('w') as outfile:
            ignore = ('CONFIGURATION_H_VERSION', 'CONFIGURATION_ADV_H_VERSION',
                      'CONFIG_EXPORT')
            filegrp = {
                'Configuration.h': 'config:basic',
                'Configuration_adv.h': 'config:advanced'
            }
            vers = defines["CONFIGURATION_H_VERSION"]
            dt_string = datetime.now().strftime("%Y-%m-%d at %H:%M:%S")
            ini_fmt = '{0:40}{1}\n'
            outfile.write(
                '#\n' + '# Marlin Firmware\n' +
                '# config.ini - Options to apply before the build\n' + '#\n' +
                f'# Generated by Marlin build on {dt_string}\n' + '#\n' +
                '\n' + '[config:base]\n' +
                ini_fmt.format('ini_use_config', ' = all') +
                ini_fmt.format('ini_config_vers', f' = {vers}'))
            # Loop through the data array of arrays
            for header in data:
                if header.startswith('__'):
                    continue
                outfile.write('\n[' + filegrp[header] + ']\n')
                for key in sorted(data[header]):
                    if key not in ignore:
                        val = 'on' if data[header][key] == '' else data[
                            header][key]
                        outfile.write(ini_fmt.format(key.lower(), ' = ' + val))

    #
    # Produce a schema.json file if CONFIG_EXPORT == 3
    #
    if config_dump >= 3:
        try:
            conf_schema = schema.extract()
        except Exception as exc:
            print("Error: " + str(exc))
            conf_schema = None

        if conf_schema:
            #
            # Produce a schema.json file if CONFIG_EXPORT == 3
            #
            if config_dump in (3, 13):
                print("Generating schema.json ...")
                schema.dump_json(conf_schema, build_path / 'schema.json')
                if config_dump == 13:
                    schema.group_options(conf_schema)
                    schema.dump_json(conf_schema,
                                     build_path / 'schema_grouped.json')

            #
            # Produce a schema.yml file if CONFIG_EXPORT == 4
            #
            elif config_dump == 4:
                print("Generating schema.yml ...")
                try:
                    import yaml
                except ImportError:
                    env.Execute(
                        env.VerboseAction(
                            '$PYTHONEXE -m pip install "pyyaml"',
                            "Installing YAML for schema.yml export",
                        ))
                    import yaml
                schema.dump_yaml(conf_schema, build_path / 'schema.yml')

    # Append the source code version and date
    data['VERSION'] = {}
    data['VERSION']['DETAILED_BUILD_VERSION'] = resolved_defines[
        'DETAILED_BUILD_VERSION']
    data['VERSION']['STRING_DISTRIBUTION_DATE'] = resolved_defines[
        'STRING_DISTRIBUTION_DATE']
    try:
        curver = subprocess.check_output(
            ["git", "describe", "--match=NeVeRmAtCh", "--always"]).strip()
        data['VERSION']['GIT_REF'] = curver.decode()
    except:
        pass

    #
    # Produce a JSON file for CONFIGURATION_EMBEDDING or CONFIG_EXPORT == 1
    #
    if config_dump == 1 or 'CONFIGURATION_EMBEDDING' in defines:
        with marlin_json.open('w') as outfile:
            json.dump(data, outfile, separators=(',', ':'))

    #
    # The rest only applies to CONFIGURATION_EMBEDDING
    #
    if not 'CONFIGURATION_EMBEDDING' in defines:
        return

    # Compress the JSON file as much as we can
    compress_file(marlin_json, marlin_zip)

    # Generate a C source file for storing this array
    with open('Marlin/src/mczip.h', 'wb') as result_file:
        result_file.write(
            b'#ifndef NO_CONFIGURATION_EMBEDDING_WARNING\n' +
            b'  #warning "Generated file \'mc.zip\' is embedded (Define NO_CONFIGURATION_EMBEDDING_WARNING to suppress this warning.)"\n'
            + b'#endif\n' + b'const unsigned char mc_zip[] PROGMEM = {\n ')
        count = 0
        for b in (build_path / 'mc.zip').open('rb').read():
            result_file.write(b' 0x%02X,' % b)
            count += 1
            if count % 16 == 0:
                result_file.write(b'\n ')
        if count % 16:
            result_file.write(b'\n')
        result_file.write(b'};\n')
Exemple #3
0
def compute_build_signature(env):
	if 'BUILD_SIGNATURE' in env:
		return

	# Definitions from these files will be kept
	files_to_keep = [ 'Marlin/Configuration.h', 'Marlin/Configuration_adv.h' ]

	build_dir=os.path.join(env['PROJECT_BUILD_DIR'], env['PIOENV'])

	# Check if we can skip processing
	hashes = ''
	for header in files_to_keep:
		hashes += get_file_sha256sum(header)[0:10]

	marlin_json = os.path.join(build_dir, 'marlin_config.json')
	marlin_zip = os.path.join(build_dir, 'mc')

	# Read existing config file
	try:
		with open(marlin_json, 'r') as infile:
			conf = json.load(infile)
			if conf['__INITIAL_HASH'] == hashes:
				# Same configuration, skip recomputing the building signature
				compress_file(marlin_json, marlin_zip)
				return
	except:
		pass

	# Get enabled config options based on preprocessor
	from preprocessor import run_preprocessor
	complete_cfg = run_preprocessor(env)

	# Dumb #define extraction from the configuration files
	real_defines = {}
	all_defines = []
	for header in files_to_keep:
		defines = extract_defines(header)
		# To filter only the define we want
		all_defines = all_defines + defines
		# To remember from which file it cames from
		real_defines[header.split('/')[-1]] = defines

	r = re.compile(r"\(+(\s*-*\s*_.*)\)+")

	# First step is to collect all valid macros
	defines = {}
	for line in complete_cfg:

		# Split the define from the value
		key_val = line[8:].strip().decode().split(' ')
		key, value = key_val[0], ' '.join(key_val[1:])

		# Ignore values starting with two underscore, since it's low level
		if len(key) > 2 and key[0:2] == "__" :
			continue
		# Ignore values containing a parenthesis (likely a function macro)
		if '(' in key and ')' in key:
			continue

		# Then filter dumb values
		if r.match(value):
			continue

		defines[key] = value if len(value) else ""

	if not 'CONFIGURATION_EMBEDDING' in defines:
		return

	# Second step is to filter useless macro
	resolved_defines = {}
	for key in defines:
		# Remove all boards now
		if key[0:6] == "BOARD_" and key != "BOARD_INFO_NAME":
			continue
		# Remove all keys ending by "_NAME" as it does not make a difference to the configuration
		if key[-5:] == "_NAME" and key != "CUSTOM_MACHINE_NAME":
			continue
		# Remove all keys ending by "_T_DECLARED" as it's a copy of not important system stuff
		if key[-11:] == "_T_DECLARED":
			continue
		# Remove keys that are not in the #define list in the Configuration list
		if not (key in all_defines) and key != "DETAILED_BUILD_VERSION" and key != "STRING_DISTRIBUTION_DATE":
			continue

		# Don't be that smart guy here
		resolved_defines[key] = defines[key]

	# Generate a build signature now
	# We are making an object that's a bit more complex than a basic dictionary here
	data = {}
	data['__INITIAL_HASH'] = hashes
	# First create a key for each header here
	for header in real_defines:
		data[header] = {}

	# Then populate the object where each key is going to (that's a O(N^2) algorithm here...)
	for key in resolved_defines:
		for header in real_defines:
			if key in real_defines[header]:
				data[header][key] = resolved_defines[key]

	# Append the source code version and date
	data['VERSION'] = {}
	data['VERSION']['DETAILED_BUILD_VERSION'] = resolved_defines['DETAILED_BUILD_VERSION']
	data['VERSION']['STRING_DISTRIBUTION_DATE'] = resolved_defines['STRING_DISTRIBUTION_DATE']
	try:
		curver = subprocess.check_output(["git", "describe", "--match=NeVeRmAtCh", "--always"]).strip()
		data['VERSION']['GIT_REF'] = curver.decode()
	except:
		pass

	with open(marlin_json, 'w') as outfile:
		json.dump(data, outfile, separators=(',', ':'))

	# Compress the JSON file as much as we can
	compress_file(marlin_json, marlin_zip)

	# Generate a C source file for storing this array
	with open('Marlin/src/mczip.h','wb') as result_file:
		result_file.write(b'#warning "Generated file \'mc.zip\' is embedded"\n')
		result_file.write(b'const unsigned char mc_zip[] PROGMEM = {\n ')
		count = 0
		for b in open(os.path.join(build_dir, 'mc.zip'), 'rb').read():
			result_file.write(b' 0x%02X,' % b)
			count += 1
			if (count % 16 == 0):
			 	result_file.write(b'\n ')
		if (count % 16):
			result_file.write(b'\n')
		result_file.write(b'};\n')