2013-10-10 49 views
3

我有一個定製生成器,它使用一個工具來創建使用源代碼和帶有markdown文檔的文本文件的文檔。即使文件已被更改,定製SCons生成器報告依賴關係也是如此

該工具需要一個指定所有輸入文件和輸出選項的配置文件。

運行時,它會在標記爲html的文件夾中生成文檔。

我的建設者有一個掃描儀找到了所有輸入文件

和發射器來設置輸出目錄。

掃描儀和發射器查找所有需要的文件。但是,當我重建它不檢測輸入文件的變化。

我公司生產能重現問題放在一個目錄下面的建設者:

gen_doc.py

import SCons.Builder 
import os 
import ConfigParser 

def _doc_build_function(target, source, env): 
    #print '***** Builder *****' 
    config = ConfigParser.SafeConfigParser() 
    try: 
     fp = open(str(source[0]), 'r') 
     config.readfp(fp) 
    finally: 
     fp.close() 
    output_dir = '' 
    if config.has_option('output_options', 'output_dir'): 
     output_dir = config.get('output_options', 'output_dir') 
    input_files = [] 
    if config.has_option('input_options', 'input'): 
     input_files = config.get('input_options', 'input').split() 
    if not os.path.exists(output_dir): 
     os.makedirs(output_dir) 

    with open(output_dir + os.sep + 'index.html', 'wb') as out_file: 
     for file in input_files: 
      try: 
       in_file = open(file, 'r') 
       out_file.write(in_file.read()) 
      finally: 
       in_file.close() 


def _doc_scanner(node, env, path): 
    source = [] 
    config = ConfigParser.SafeConfigParser() 
    try: 
     fp = open(str(node), 'r') 
     config.readfp(fp) 
    finally: 
     fp.close() 
    if config.has_option('input_options', 'input'): 
     for i in config.get('input_options', 'input').split(): 
      source.append(os.path.abspath(i)) 
    return source 

def _doc_emitter(target, source, env): 
    target = [] 
    config = ConfigParser.SafeConfigParser() 
    try: 
     fp = open(str(source[0]), 'r') 
     config.readfp(fp) 
    finally: 
     fp.close() 
    if config.has_option('output_options', 'output_dir'): 
     target.append(env.Dir(os.path.abspath(config.get('output_options', 'output_dir')))) 
     env.Clean(source, env.Dir(os.path.abspath(config.get('output_options', 'output_dir')))) 

    return target, source 


def generate(env): 
    doc_scanner = env.Scanner(function = _doc_scanner) 

    doc_builder = SCons.Builder.Builder(
     action = _doc_build_function, 
     emitter = _doc_emitter, 
     source_scanner = doc_scanner, 
     single_source = 1 
    ) 

    env.Append(BUILDERS = { 
     'gen_doc': doc_builder, 
    }) 

def exists(env): 
    '''Using internal builder''' 
    return True 

SConstruct

env = Environment() 
env.Tool('gen_doc', toolpath=['.']) 
env.gen_doc('config_doc') 

config_doc

[input_options] 
input = a.md b.md 

[output_options] 
output_dir = html 

a.md

Hello 

b.md

world 

當我運行這個它產生正確輸出

文件夾html在一個文件中有一個名爲 'index.html的'

用字Hello world

當我運行

scons -n tree=status html 

我得到以下

scons: Reading SConscript files ... 
scons: done reading SConscript files. 
scons: Building targets ... 
scons: `html' is up to date. 
E   = exists 
    R  = exists in repository only 
    b  = implicit builder 
    B  = explicit builder 
    S  = side effect 
    P  = precious 
     A = always build 
     C = current 
     N = no clean 
     H = no cache 

[E B C ]+-html 
[E  C ] +-config_doc 
[E  C ] +-a.md 
[E  C ] +-b.md 
scons: done building targets. 

我進去修改b.md文件並重新運行

scons -n tree=status html 

輸出是它仍然報告b.md爲最新版本,因此文檔不會被重建。

有沒有辦法讓scons看到掃描儀看到的源文件的變化,並在文件改變時重建?

更新

我做了一點玩弄我創建了一個虛擬的決勝局,如果我能找出原因並沒有被添加了這些文件。

def foo(dependency, target, prev_ni): 
    print 'dependency = %s' % (dependency) 
    print 'target = %s' % (target) 
    return True 

「生成(ENV)」我添加行「env.Decider(富)」

添加到樹由_doc_scanner的文件不會被調用決勝局功能,所以一個MD5哈希是從未被計算。

我能做些什麼使這些文件做來電決勝局?

UPDATE2:

忘記使哨所時添加的回報發射器。

更新3

修改了代碼,以便它不再調用外部建設者。它現在調用一個模擬構建器的內部構建器函數。這只是模擬外部構建器的行爲。原始構建行動是action = 'cd ${SOURCE.dir} && gen_docs ${SOURCE.file}

回答

3

這是什麼,我會考慮在SCons的一個設計缺陷造成的:目錄節點是總是認爲,如果該目錄存在了最新的。從SCons FAQ

相關章節:

爲什麼我的目錄只更新一次?

像其他的構建系統,使用SCons認爲作爲一個目標,如果它存在了最新的目錄。你第一次建立時,目錄不在那裏,所以SCons運行了更新命令。每次之後,該目錄已經存在,所以SCons認爲它是最新的。

你可以解決這個問題,認爲這有點痛苦。對於您要參與依賴關係圖的每個目錄,您需要創建一個「表示」該目錄的虛擬文件。無論何時生成目錄,都要寫入文件。取決於文件而不是目錄。

您的代碼可以更新要做到這一點,正是如此:

import SCons.Builder 
import os 
import ConfigParser 
import datetime 

def _manifest(target): 
    return os.path.join('.manifest', str(target)) 

def _touch(path): 
    dirname = os.path.dirname(path) 
    if not os.path.exists(dirname): 
     os.makedirs(dirname) 
    with open(path, 'wt') as f: 
     f.write(str(datetime.datetime.now())) 

def _doc_build_function(target, source, env): 
    #print '***** Builder *****' 
    config = ConfigParser.SafeConfigParser() 
    try: 
     fp = open(str(source[0]), 'r') 
     config.readfp(fp) 
    finally: 
     fp.close() 
    output_dir = '' 
    if config.has_option('output_options', 'output_dir'): 
     output_dir = config.get('output_options', 'output_dir') 
    input_files = [] 
    if config.has_option('input_options', 'input'): 
     input_files = config.get('input_options', 'input').split() 
    if not os.path.exists(output_dir): 
     os.makedirs(output_dir) 

    with open(output_dir + os.sep + 'index.html', 'wb') as out_file: 
     for file in input_files: 
      try: 
       in_file = open(file, 'r') 
       out_file.write(in_file.read()) 
      finally: 
       in_file.close() 

    for t in target: 
     _touch(_manifest(t)) 


def _doc_scanner(node, env, path): 
    source = [] 
    config = ConfigParser.SafeConfigParser() 
    try: 
     fp = open(str(node), 'r') 
     config.readfp(fp) 
    finally: 
     fp.close() 
    if config.has_option('input_options', 'input'): 
     for i in config.get('input_options', 'input').split(): 
      source.append(os.path.abspath(i)) 
    return source 

def _doc_emitter(target, source, env): 
    target = [] 
    config = ConfigParser.SafeConfigParser() 
    try: 
     fp = open(str(source[0]), 'r') 
     config.readfp(fp) 
    finally: 
     fp.close() 
    if config.has_option('output_options', 'output_dir'): 
     target.append(env.Dir(os.path.abspath(config.get('output_options', 'output_dir')))) 
     env.Clean(source, env.Dir(os.path.abspath(config.get('output_options', 'output_dir')))) 

    target.extend(map(_manifest, target)) 

    return target, source 


def generate(env): 
    doc_scanner = env.Scanner(function = _doc_scanner) 

    doc_builder = SCons.Builder.Builder(
     action = _doc_build_function, 
     emitter = _doc_emitter, 
     source_scanner = doc_scanner, 
     single_source = 1 
    ) 

    env.Append(BUILDERS = { 
     'gen_doc': doc_builder, 
    }) 

def exists(env): 
    '''Using internal builder''' 
    return True 
+0

我是從我的電腦走了一段時間,所以它可能是一個,而我能夠嘗試了這一點之前。我一直認爲這是一個源代碼問題,即使我知道你指出的FAQ,也從來沒有想過目錄節點。將嘗試並讓你知道我的結果。我認爲gen_docs工具已經創建了一個包含日期的文件,所以我可以繞過創建自己的日期。 – gnash117

0

您的發射沒有返回修改的目標,源列表。

更多信息,請參見http://www.scons.org/doc/HTML/scons-user/x3798.html

發射功能應該返回的是 應建立目標和源的修改單從該目標,將建成。

+0

您將我放上去碼是否正確。這是重新解決問題的錯誤。 (正如我在說明中所說)我的原始代碼確實會返回目標和源代碼。我會更新這個問題,謝謝。 – gnash117

相關問題