2012-01-03 55 views
3

我在Python中有一个C++代码生成器,它生成许多源文件。大多数情况下,只有一个文件发生更改,但由于生成器会重新生成所有文件,因此它们都将重新生成。有没有办法让Python不覆盖文件,或者让cmak使用校验和来查看需要重建的内容,而不仅仅是使用文件日期?输出相同时不覆盖文件的简单方法

我在想这样的事情会很容易在Python:如果我能代替

with open('blah', 'w') as f: 

与此:

with open_but_only_overwrite_if_total_output_is_different('blah', 'w') as f: 

什么是实现这个目的的一个很好的方式?

+0

您在问cmake或Python是否可以确定文件是否应该被覆盖__before__代码已经在Python脚本中生成了? – jknupp 2012-01-03 21:32:30

+0

不,他问是否可以避免在这种情况下更改文件的修改日期,因为如果修改日期更改,构建系统(CMake)将重建文件,从而使构建时间更长。 – kindall 2012-01-03 21:38:11

+0

啊,现在有道理。 – jknupp 2012-01-03 21:40:56

回答

4

代码和Neil G想法,Petr Viktoringeccojoel3000结合:

import contextlib 
@contextlib.contextmanager 
def write_on_change(filename): 
    with tempfile.NamedTemporaryFile(delete=False) as f: 
     yield f 
     tempname = f.name 
    try: 
     overwrite = not filecmp.cmp(tempname,filename) 
    except (OSError,IOError): 
     overwrite = True 
    if overwrite: 
     shutil.copyfile(tempname,filename) 
    os.unlink(tempname) 

一些小补充(希望改进):

  • shutil.copyfile只复制的tempname内容到 filename ,同时保留元数据如文件权限和文件的所有权。
  • filecmp.cmp检查文件 的尺寸,并返回False如果大小不匹配。如果文件很大,并且其中一个东西被追加到 端,那么这可能是一个不错的 加速。它还一次读取并比较bufsize = 8*1024字节,而不是一次一行地写入 。 bufsize通常会大于 行,这会导致读取次数减少。
+0

+1,谢谢。为什么要复制而不移动? – 2012-01-05 18:11:16

+0

好主意!编辑... – unutbu 2012-01-05 18:29:05

+0

嗯......我不太清楚为什么,但是当我用/etc/test.dat以777权限测试这段代码但被另一个用户所拥有时,'shutil.copyfile'成功,但'shutil。移动“导致OSError。 – unutbu 2012-01-05 18:35:16

3

我建议你写了这样的自己的类文件对象:

  • __enter__:创建一个临时文件
  • __exit__:比较与旧文件的临时文件的内容(如果存在)如果他们是不一样的,然后通过临时文件

本文取代旧文件是相当有帮助的理解with声明:Understanding Python's "with" statement

+0

不错的解决方案。要用临时文件替换旧文件,是否有可能以某种方式将旧文件指向临时文件?而不是阅读tempfile并将其写入旧文件 – 2012-01-04 00:24:46

+0

@robertking yes:[shutil.move](http://docs.python.org/library/shutil.html#shutil.move) – gecco 2012-01-04 06:43:02

0

最简单的方法是用Python做的cmake究竟是干什么的:有发电机检查,如果输入大于输出更新,并仅生成,如果它是。

下面是我用类似的东西,一个片段:

if (os.path.exists(output) and 
    os.path.getmtime(source) <= os.path.getmtime(output)): 
    print "Generated output %s is up-to-date." % output 
    return 
+0

没有什么说他正在使用本地文件系统上的文件作为生成器的输入。实际上,这可能是任何事情。 – jknupp 2012-01-03 21:51:36

+0

生成器是一个程序,所以在运行时很难知道哪些输出文件会受到我对程序所做的更改的影响。 – 2012-01-03 21:59:24

+0

在这种情况下,要么执行@ gecco的答案。另一种选择是让你的生成器写入一个StringIO.StringIO。然后编写一个名为'write_if_different(stringio,filename)'的快速函数' – thesamet 2012-01-03 22:14:23

3

使用filecmp - http://docs.python.org/library/filecmp.html

写您的新文件到一个tmp目录,比较对你的工作目录 ,并调过来改动过的文件。然后删除tmp。

+0

啊,谢谢!就在我编写完gecco的解决方案后...... – 2012-01-03 22:42:11

+0

'filecmp'是否可以与'NamedTemporaryFile'一起使用? – 2012-01-03 22:45:03

+0

取决于。它在unix系统上效果最好(相对于win)。尝试一下。 – joel3000 2012-01-03 23:00:26