2012-08-17 42 views
0

我使用uptodate?方法來確定文件是否必須重新編譯/重新鏈接/重新生成等。有時,我使用的構建框架在這些步驟中非常快,以至於輸出文件和先決條件文件具有完全相同的文件修改時間戳。這會導致構建框架不必要地重新編譯/重新鏈接文件。FileUtils uptodate?問題在紅寶石

我比較時間戳建設像這樣 -

compile_file(file) unless uptodate?(file, %W(#{dependencies})) 

我擡起頭來UPTODATE源?從here看起來像這樣 -

def uptodate?(new, old_list, options = nil) 
    raise ArgumentError, 'uptodate? does not accept any option' if options 
    return false unless File.exist?(new) 
    new_time = File.mtime(new) 
    old_list.each do |old| 
    if File.exist?(old) 
     return false unless new_time > File.mtime(old) 
    end 
    end 
    true 
end 

如果懷疑,如果時間戳相等,則返回false。解決這個問題最優雅的方式是什麼?我曾嘗試在Linux和Windows上運行該框架,並遇到同樣的問題。從我讀的here這不太可能是文件系統特定的文件修改時間分辨率問題(因爲ext4的分辨率爲1微秒)。

+0

這段代碼很奇怪,爲什麼接受選項是因爲人們提供錯誤時會引發錯誤?而且我不明白這個問題,如果構建框架如此之快,那麼爲什麼偶爾重新編譯文件會影響它?除非你說它在同一個版本中多次重新編譯同一個文件。如果是這種情況,那麼就考慮轉向更好的工具。例如,[Rake](http://jasonseifer.com/2010/04/06/rake-tutorial?again)是爲此目的而構建的,並通過依賴關係處理這個問題,所以它不會有這樣的問題。 – 2012-08-17 04:50:06

+0

@JoshuaCheek該代碼是Ruby中FileUtils模塊的一部分(即它不是由我寫的!)。我使用Rake,但是我懷疑即使我指定了文件依賴關係(我猜它在內部使用uptodate?)它的行爲也是一樣的。 我不希望構建框架不必要地重新編譯/重新鏈接/運行單元測試,因爲當你TDD'ing它確實很重要! – thegreendroid 2012-08-17 05:49:20

+0

男人,在stdlib O.o中有一些奇怪的代碼無論如何,差異(如果我正確理解你在做什麼)將是Rake將僅檢查一次該文件,例如,它只會編譯一次,這是因爲rake任務不像方法,而是像依賴關係。文件a取決於c,文件b取決於c。如果你編譯了a和b,那麼c將被編譯一次,因此c的依賴性得到滿足。 – 2012-08-17 06:07:35

回答

1

uptodate?似乎是正確的。如果時間戳相同,則文件可能是最新的,或者它可能不是最新的。因此它在安全方面犯錯並宣佈它已過時。

在構建文件的步驟中,如果與構建文件相關的時間相等,則添加邏輯以使構建文件的時間提前一秒。是的,這是一種黑客攻擊,但可能是你在這種情況下可以做的最好的。

某些文件系統(如ext4等)可以存儲時間戳以亞秒級分辨率,可能值得一看。

除了上述任何一個,你可以猴子補丁uptodate?或者只是在時間戳相同的情況下創建自己的錯誤。

+0

謝謝,你的建議解決方案應該工作得很好:)我同意這有點不好,但它會保持我的框架快速運行,這是非常重要的(因爲我做TDD)。 – thegreendroid 2012-08-17 05:53:10

2

似乎Ruby的File對象不支持文件修改時間戳的亞秒級分辨率,即使底層文件系統確實如此。一個解決辦法是掏出來ls --full-time並使用解析結果Ruby的DateTime,這確實秒支持分數:

module FileUtilsPlus 
    def self.uptodate?(new, old_list, options = nil) 
    return true if FileUtils.uptodate?(new, old_list, options) 
    return false unless File.exist?(new) 
    new_time = filemtime(new) 
    old_list.each do |old| 
     if File.exist?(old) 
     return false unless new_time > filemtime(old) 
     end 
    end 
    end 

    def self.filemtime(path) 
    DateTime.parse(`ls --full-time foo | awk '{ print $6 " " $7 }'`) 
    end 
end 

注意,這將是多少,比Ruby的本地文件操作慢得多,所以我們只有在FileUtils::uptodate?返回false時纔會這樣做。

+0

謝謝你的深刻解答,我接受@WayneConrad的其他答案,因爲它是一個輕量級/快速的解決方案。 – thegreendroid 2012-08-17 05:51:20