2017-03-20 28 views
2

我有兩個C語言文件:foo.cbar.h(可能還有許多其他文件)。我想要一個在foo.c中使用並在bar.h中定義的所有預處理器宏的列表。如何確定foo.c中的哪些預處理器宏源自bar.h?

或者,如果那是太難了,甚至它同時出現在foo.cbar.h所有預處理宏(但不任何標識,單詞或一段文字)的列表。

我該如何獲得?

+0

我不認爲這有一個普遍的答案,回答這種問題很難,這是一個好的主意,因爲它是一個好主意(例如'BAR_BLAH()'以表明BLAH )'宏來自'bar.h')。您可以嘗試刪除'#include「bar.h」'並查看是否有未定義的引用錯誤。 – unwind

+0

@unwind:如果我控制了名字,我可能沒有這個問題......這不是我自己的代碼。 – einpoklum

+0

gcc特定的技術是否可以接受? – rici

回答

2

您可以從gcc的預處理選項的輸出中拼湊這些信息。

爲了得到實際使用的文件,你可以使用-E -dU選項,預處理文件宏的列表,也包括在任何宏的第一使用#define命令。 (它也產生#undef命令與#ifdef#if defined(...)測試未定義的名字。)(必須使用-E選項 - 僅預處理 - 爲-dU要妥善處理。)

由於-dU不抑制預處理輸出,您只需查看#define指令就可以對其進行過濾。對於某些應用程序,您可能還希望通過僅查看相關文件中實際使用的用途來進一步對其進行過濾,因爲報告還包括通過包含文件使用宏。但是在這種情況下,與頭文件中實際定義的宏的交集可能就足夠了。

所以習慣宏的列表中file.c

gcc -E -dU file.c | grep -Eo '^#define [_A-Za-z][_A-Za-z0-9]*' 

(grep的-Eo刪除宏定義。)

可以近似的頭實際上被定義宏的列表文件中使用稍微大方grep調用,這樣的事情:

grep -Eo '^\s*#\s*define\s+[_A-Za-z][_A-Za-z0-9]*' header.h 

這將拿起宏DEFI即使條件失敗,也會在條件部分中進行劃分,並且會在註釋中看到類似#define指令的行。通常,這些都不會造成很多問題。

您可以使用gcc的-E -dM-E -dD選項來獲取標題中所有定義的列表,但這兩個選項都會插入由標題包含的標題定義的宏。 (-dM也包含預定義的宏。)因此,除非您對由於包含頭文件而定義的所有宏感興趣,否則您確實需要做更多工作來專注於頭文件實際定義的宏。

然後你只需要找到兩個列表的交集。一種方法是單獨提取宏名稱(awk '{print $2}'),sort -u兩個列表,然後合併它們,最後通過uniq -d傳遞它們,只查看兩個列表中的條目。 (以下兩個限定殼功能used_and_defined你會調用used_and_defined foo.c bar.h

used() { 
    gcc -E -dU "$1" | 
    grep -Eo '^#define [_A-Za-z][_A-Za-z0-9]*' | 
    cut -f2 -d' ' | 
    sort -u 
} 

defined() { 
    grep -Eo '^\s*#\s*define\s+[_A-Za-z][_A-Za-z0-9]*' "$1" | 
    awk '{ print $2 }' | 
    sort -u 
} 

used_and_defined() { 
    cat <(used "$1") <(defined "$2") | sort | uniq -d 
} 

或者你可以完成整個操作與awk

used_and_defined() { 
    awk '/^[[:space:]]*#[[:space:]]*define/ { 
     gsub(/[ (].*/, "", $2); 
     if (NR == FNR) ++macros[$2]; 
     else if (macros[$2]) print $2; 
     }' \ 
     <(grep -Eo '^\s*#\s*define\s+[_A-Za-z][_A-Za-z0-9]*' "$2") \ 
     <(gcc -E -dU "$1") 
} 
+0

您可以擴展您的答案以提醒讀者「-E」和「-dU」的含義嗎?此外,您的近似值是我迄今爲止所做的 - 對兩個文件執行一些正則表達式,然後清理該行,對uniq進行排序並獲得大致的宏名稱列表 - 然後使用一些uniq -c hackery獲得差異 – einpoklum

+0

@einpoklum:Ok ,添加了對該選項的簡要說明,並且沒有經過非常嚴格的測試。 – rici

+0

第二個awk腳本 - 爲什麼在標準輸入上連接兩個文件是有意義的(假設這就是您在那裏做的)? – einpoklum

1

一種策略(誠然笨重)可以是:

的foreach宏標識符SOME_MACRObar.h,運行

gcc -E -DSOME_MACRO=recognizable_value foo.c | grep recognizable_value 

即預處理源和檢測是否發生了膨脹。請注意,這對於僅在#if指令等中使用的宏不起作用。

0

一個gcc特異性(但clang有相同的選項相同的語義)是使用-dD除了-E

gcc -E -dD -o foo.i [other options] foo.c 

將在輸出中保留#define行,以及# nnn "/path/to/file.h"指令,以便您應該能夠確定哪個宏屬於哪個文件。如果你想提取來自bar.h的宏,根據你期望在bar.h中找到的宏的數量,你最喜歡的編輯器的搜索命令可能就足夠了,或者一個小的awk/perl/python/...腳本會幫幫我。