12f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar"""
22f4415e256274e00b3fd359f2ba395d401079122Daniel DunbarDefines utilities useful for performing standard "configuration" style tasks.
32f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar"""
42f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar
52f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbarimport re
62f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbarimport os
72f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar
82f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbardef configure_file(input_path, output_path, substitutions):
92f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar    """configure_file(input_path, output_path, substitutions) -> bool
102f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar
112f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar    Given an input and output path, "configure" the file at the given input path
122f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar    by replacing variables in the file with those given in the substitutions
132f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar    list. Returns true if the output file was written.
142f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar
152f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar    The substitutions list should be given as a list of tuples (regex string,
162f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar    replacement), where the regex and replacement will be used as in 're.sub' to
172f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar    execute the variable replacement.
182f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar
192f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar    The output path's parent directory need not exist (it will be created).
202f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar
212f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar    If the output path does exist and the configured data is not different than
222f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar    it's current contents, the output file will not be modified. This is
232f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar    designed to limit the impact of configured files on build dependencies.
242f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar    """
252f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar
262f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar    # Read in the input data.
272f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar    f = open(input_path, "rb")
282f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar    try:
292f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar        data = f.read()
302f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar    finally:
312f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar        f.close()
322f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar
332f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar    # Perform the substitutions.
342f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar    for regex_string,replacement in substitutions:
352f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar        regex = re.compile(regex_string)
362f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar        data = regex.sub(replacement, data)
372f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar
382f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar    # Ensure the output parent directory exists.
392f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar    output_parent_path = os.path.dirname(os.path.abspath(output_path))
402f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar    if not os.path.exists(output_parent_path):
412f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar        os.makedirs(output_parent_path)
422f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar
432f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar    # If the output path exists, load it and compare to the configured contents.
442f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar    if os.path.exists(output_path):
452f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar        current_data = None
462f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar        try:
472f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar            f = open(output_path, "rb")
482f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar            try:
492f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar                current_data = f.read()
502f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar            except:
512f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar                current_data = None
522f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar            f.close()
532f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar        except:
542f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar            current_data = None
552f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar
562f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar        if current_data is not None and current_data == data:
572f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar            return False
582f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar
592f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar    # Write the output contents.
602f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar    f = open(output_path, "wb")
612f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar    try:
622f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar        f.write(data)
632f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar    finally:
642f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar        f.close()
652f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar
662f4415e256274e00b3fd359f2ba395d401079122Daniel Dunbar    return True
67