2011-07-28 34 views
7

Có cách nào tốt nhất để hỗ trợ các phụ thuộc vào các cờ tiền xử lý C/C++ như -DCOMPILE_WITHOUT_FOO? Dưới đây là vấn đề của tôi:Thực tiễn tốt nhất cho các phụ thuộc vào #defines?

> setenv COMPILE_WITHOUT_FOO 
> make <Make system reads environment, sets -DCOMPILE_WITHOUT_FOO> 
    <Compiles nothing, since no source file has changed> 

Những gì tôi muốn làm là có tất cả các file mà dựa vào #ifdef báo cáo được biên dịch lại:

> setenv COMPILE_WITHOUT_FOO 
> make 
    g++ FileWithIfdefFoo.cpp 

Những gì tôi không muốn là phải biên dịch lại tất cả mọi thứ nếu giá trị của COMPILE_WITHOUT_FOO chưa thay đổi.

Tôi có một tập lệnh Python nguyên thủy hoạt động (xem bên dưới) về cơ bản viết một tệp tiêu đề FooDefines.h và sau đó phân biệt nó để xem có điều gì khác biệt không. Nếu có, nó sẽ thay thế FooDefines.h và sau đó phụ thuộc vào tệp nguồn thông thường sẽ thay thế. Định nghĩa là không phải được truyền trên dòng lệnh với -D. Điểm bất lợi là bây giờ tôi phải bao gồm FooDefines.h trong bất kỳ tệp nguồn nào sử dụng #ifdef và tôi cũng có tệp tiêu đề mới được tạo động cho mỗi #ifdef. Nếu có một công cụ để làm điều này, hoặc một cách để tránh sử dụng bộ tiền xử lý, tôi là tất cả các tai.

import os, sys 
def makeDefineFile(filename, text): 
    tmpDefineFile = "/tmp/%s%s"%(os.getenv("USER"),filename) #Use os.tempnam? 
    existingDefineFile = filename 

    output = open(tmpDefineFile,'w') 
    output.write(text) 
    output.close() 

    status = os.system("diff -q %s %s"%(tmpDefineFile, existingDefineFile)) 

    def checkStatus(status): 
     failed = False 
     if os.WIFEXITED(status): 
      #Check return code 
      returnCode = os.WEXITSTATUS(status) 
      failed = returnCode != 0 
     else: 
      #Caught a signal, coredump, etc. 
      failed = True 
     return failed,status 

    #If we failed for any reason (file didn't exist, different, etc.) 
    if checkStatus(status)[0]: 
     #Copy our tmp into the new file 
     status = os.system("cp %s %s"%(tmpDefineFile, existingDefineFile)) 
     failed,status = checkStatus(status) 
     print failed, status 
     if failed: 
      print "ERROR: Could not update define in makeDefine.py" 
      sys.exit(status) 

Trả lời

0

kích hoạt dấu ngày trên tệp. Một tệp phụ thuộc mới hơn tệp phụ thuộc vào nó sẽ kích hoạt nó để biên dịch lại. Bạn sẽ phải đặt định nghĩa của mình cho mỗi tùy chọn trong một tệp .h riêng biệt và đảm bảo rằng những phụ thuộc đó được biểu diễn trong tệp makefile. Sau đó, nếu bạn thay đổi một tùy chọn, các tệp phụ thuộc vào nó sẽ được biên dịch lại tự động.

Nếu tính đến các tệp bao gồm các tệp, bạn sẽ không phải thay đổi cấu trúc của nguồn. Bạn có thể bao gồm tệp "BuildSettings.h" bao gồm tất cả các tệp cài đặt riêng lẻ.

Vấn đề khó khăn duy nhất sẽ là nếu bạn làm cho nó đủ thông minh để phân tích cú pháp include guards. Tôi đã nhìn thấy vấn đề với biên dịch vì bao gồm va chạm tên tập tin và thứ tự bao gồm tìm kiếm thư mục.

Bây giờ bạn đề cập đến nó, tôi nên kiểm tra xem IDE của tôi có đủ thông minh để tự động tạo các phụ thuộc đó cho tôi hay không. Âm thanh như một điều tuyệt vời để thêm vào một IDE.

+0

đã thêm báo cáo lỗi cho IDE mà tôi sử dụng. Với bất kỳ may mắn nào họ có thể sửa chữa nó sớm – Jay

2

Tôi không tin rằng có thể xác định tự động. Các chỉ thị tiền xử lý không được biên dịch thành bất kỳ thứ gì. Nói chung, tôi mong đợi để làm một biên dịch đầy đủ nếu tôi phụ thuộc vào một định nghĩa. DEBUG là một ví dụ quen thuộc.

Tôi không nghĩ rằng có phải cách để thực hiện điều đó. Nếu bạn không thể làm đúng cách, thì cách ngu ngốc nhất có thể là lựa chọn tốt nhất của bạn. Tìm kiếm văn bản cho COMPILE_WITH_FOO và tạo phụ thuộc theo cách đó. Tôi sẽ phân loại này như là một shenanigan và nếu bạn đang viết mã chia sẻ, tôi sẽ khuyên bạn nên tìm mua khá đáng kể trong từ đồng nghiệp của bạn.

CMake có một số tiện nghi có thể làm cho việc này trở nên dễ dàng hơn. Bạn sẽ tạo một mục tiêu tùy chỉnh để thực hiện việc này. Bạn có thể giao dịch các vấn đề ở đây mặc dù, duy trì một danh sách các tập tin phụ thuộc vào biểu tượng của bạn. Tìm kiếm văn bản của bạn có thể tạo ra tập tin đó nếu nó thay đổi mặc dù. Tôi đã sử dụng các kỹ thuật tương tự kiểm tra xem tôi có cần xây dựng lại các kho dữ liệu tĩnh dựa trên các dấu thời gian của wget hay không.

Cheetah là một công cụ hữu ích khác.

Nếu đó là tôi, tôi nghĩ tôi sẽ làm lại toàn bộ.

+0

Thật không may, tôi nghĩ rằng bạn là đúng. Trong nhóm này, sẽ không có vấn đề gì khi thi hành việc sử dụng một cơ chế (đặc biệt là kể từ khi tôi xây dựng hệ thống tạo của chúng tôi [trên đầu trang của QMake]). Tuy nhiên, không có một cách tiêu chuẩn hoặc thậm chí còn được ưa thích để làm điều này. Tuy nhiên, một lý do khác mà bộ tiền xử lý chỉ nên được sử dụng khi cần thiết. –

+0

Việc biên dịch có điều kiện luôn luôn tẻ nhạt. Sử dụng nó một cách tiết kiệm với các bản xây dựng hàng ngày/tích hợp là giải pháp ít áp bức nhất mà tôi đã thấy với điều này. Thông báo trước rằng hầu hết các tổ chức không được thiết lập để thực hiện điều này trên các nhánh riêng. –

3

Điều này chắc chắn không phải là cách tiếp cận đẹp nhất, nhưng nó sẽ làm việc:

find . -name '*cpp' -o -name '*h' -exec grep -l COMPILE_WITHOUT_FOO {} \; | xargs touch 

Điều đó sẽ xem xét thông qua mã nguồn của bạn cho vĩ mô COMPILE_WITHOUT_FOO, và "chạm" mỗi tập tin, mà sẽ cập nhật các dấu thời gian. Sau đó, khi bạn chạy, các tệp đó sẽ biên dịch lại.

Nếu bạn đã ack cài đặt, bạn có thể đơn giản hóa lệnh này:

ack -l --cpp COMPILE_WITHOUT_FOO | xargs touch 
+0

Đó là một cách tiếp cận thú vị. Tôi nghĩ rằng grep là đủ bởi vì các phụ thuộc có xu hướng bằng phẳng, thay vì phụ thuộc vào nhau. Thật không may cho tôi, tôi có Perforce để lại các tệp ở trạng thái chỉ đọc trừ khi chúng được kiểm tra. Nếu tôi đã sử dụng git giải pháp của bạn sẽ được hoàn hảo. –

+0

Boo cho perforce – ojblass

+0

@Walter - Tôi cũng sử dụng Perforce. Bạn sẽ có thể kiểm tra toàn bộ thư mục, cho phép bạn sử dụng thư mục này. Nhưng khi bạn kiểm tra các thay đổi của mình, trước hết hãy nhớ thực hiện "Hoàn nguyên các tệp chưa thay đổi". – Tim

0

Jay chỉ ra rằng "làm trigger trên tem thời gian ngày trên các tập tin".

Về mặt lý thuyết, bạn có thể có makefile chính của mình, gọi nó là m1, bao gồm các biến từ một tệp makefile thứ hai được gọi là m2. m2 sẽ chứa một danh sách tất cả các cờ tiền xử lý.

Bạn có thể có quy tắc thực hiện cho chương trình của mình tùy thuộc vào m2 được cập nhật.

quy tắc tạo m2 sẽ là nhập tất cả các biến môi trường (và do đó #include chỉ thị).

mẹo sẽ là, quy tắc tạo m2 sẽ phát hiện nếu có sự khác biệt so với phiên bản trước. Nếu vậy, nó sẽ cho phép một biến có thể buộc "làm tất cả" và/hoặc làm sạch cho mục tiêu chính. nếu không, nó sẽ chỉ cập nhật dấu thời gian trên m2 và không kích hoạt bản làm lại đầy đủ.

cuối cùng, quy tắc cho mục tiêu bình thường (thực hiện tất cả) sẽ nguồn trong chỉ thị tiền xử lý từ m2 và áp dụng chúng theo yêu cầu.

điều này nghe có vẻ dễ dàng/có thể về lý thuyết, nhưng trong thực tế, GNU Make là khó khăn hơn nhiều để có được loại công cụ này để làm việc. Tôi chắc rằng nó có thể được thực hiện mặc dù.

+0

Nó thực sự khá dễ dàng. "include cppflagsfile" và "$ (ENVDEPOBJS): cppflagsfile" đã thực hiện thủ thuật. – thiton

1

Sự cố của bạn có vẻ thích hợp để xử lý nó với autoconfautoheader, ghi giá trị của các biến vào tệp config.h. Nếu điều đó là không thể, hãy xem xét đọc chỉ thị "-D" từ một tệp và viết cờ vào tệp đó.

Trong mọi trường hợp, bạn phải tránh các bản dựng chỉ phụ thuộc vào biến môi trường. Bạn không có cách nào để nói khi môi trường thay đổi. Có một nhu cầu dứt khoát để lưu trữ các biến trong một tập tin, cách sạch sẽ nhất là bằng autoconf, autoheader và một nguồn và nhiều cây xây dựng; cách sạch thứ hai bằng cách tái configure -ing cho mỗi chuyển ngữ cảnh biên dịch; và cách thứ ba sạch sẽ nhất là một tệp chứa tất cả các trình chuyển đổi trình biên dịch có thể thay đổi mà tất cả các đối tượng phụ thuộc vào các công tắc này phụ thuộc vào chúng.

Khi bạn chọn triển khai cách thứ ba, hãy nhớ không cập nhật tệp này một cách không cần thiết, ví dụ: bằng cách xây dựng nó ở một vị trí tạm thời và sao chép nó có điều kiện về sự khác biệt, và sau đó thực hiện các quy tắc sẽ có khả năng xây dựng lại các tệp của bạn theo điều kiện tùy thuộc vào cờ.

1

Một cách để làm điều này là lưu trữ từng giá trị trước của # define trong một tệp và sử dụng điều kiện trong tệp makefile của bạn để bắt buộc cập nhật tệp đó bất cứ khi nào giá trị hiện tại không khớp với trước đó. Bất kỳ tệp nào phụ thuộc vào macro đó sẽ bao gồm tệp dưới dạng phụ thuộc.

Đây là một ví dụ.Nó sẽ cập nhật file.o nếu file.c thay đổi hoặc biến số COMPILE_WITHOUT_FOO khác với lần trước. Nó sử dụng $(shell) để so sánh giá trị hiện tại với giá trị được lưu trữ trong tệp envvars/COMPILE_WITHOUT_FOO. Nếu chúng khác nhau, thì nó tạo ra một lệnh cho tệp đó phụ thuộc vào force, luôn được cập nhật.

file.o: file.c envvars/COMPILE_WITHOUT_FOO 
    gcc -DCOMPILE_WITHOUT_FOO=$(COMPILE_WITHOUT_FOO) $< -o [email protected] 

ifneq ($(strip $(shell cat envvars/COMPILE_WITHOUT_FOO 2> /dev/null)), $(strip $(COMPILE_WITHOUT_FOO))) 
force: ; 
envvars/COMPILE_WITHOUT_FOO: force 
    echo "$(COMPILE_WITHOUT_FOO)" > envvars/COMPILE_WITHOUT_FOO 
endif 

Nếu bạn muốn hỗ trợ có macro không xác định, bạn sẽ cần phải sử dụng ifdef hoặc ifndef điều kiện, và có một số dấu hiệu cho thấy trong các tập tin rằng giá trị được undefined lần cuối cùng nó được chạy.

Các vấn đề liên quan