Inno Setup - 递归子目录而不创建相同的子目录

Inno Setup - Recurse sub directories without creating those same sub directories

我经常使用 recursesubdirs 标志遍历多个子目录并提取特定文件或文件类型,而不必单独显式引用每个文件。示例:

Source: C:\kh25\dependencies\*.dll; DestDir: {app}; Flags: recursesubdirs

这会在我的目标 {app} 路径中创建与最初检索 DLL 的源完全相同的目录结构。例如,如果我从上面的 C:\kh25\dependencies\test 中检索到一个 DLL,那么它将将该 DLL 放在 {app}\test 路径中。

可以通过以下方式修改此行为:

Source: C:\kh25\dependencies\test\*.dll; DestDir: {app}; Flags: recursesubdirs

但显然这意味着我必须单独引用依赖项中的每个子目录。

所以 64,000 美元的问题是如何防止在目标中重新创建相同的目录而不必显式引用源目录?

使用 Inno Setup preprocessor 生成 [Files] 部分的条目。

一个可能的(并且相对不错和简单的)解决方案是使用递归宏,例如:

#pragma parseroption -p-

#define FileEntry(Source) \
    "Source: " + Source + "; DestDir: {app}\n"

#define ProcessFile(Source, FindResult, FindHandle) \
    FindResult \
        ? \
            Local[0] = FindGetFileName(FindHandle), \
            Local[1] = Source + "\" + Local[0], \
            (Local[0] != "." && Local[0] != ".." \
                ? (DirExists(Local[1]) ? \
                    ProcessFolder(Local[1]) : FileEntry(Local[1])) \
                : "") + \
            ProcessFile(Source, FindNext(FindHandle), FindHandle) \
        : \
            ""

#define ProcessFolder(Source) \
    Local[0] = FindFirst(Source + "\*", faAnyFile), \
    ProcessFile(Source, Local[0], Local[0])

#pragma parseroption -p+

#emit ProcessFolder("C:\kh25\dependencies")

尽管此解决方案有其局限性,并且可能会使具有大量文件或深层目录结构的预处理器崩溃(适用于数千个文件)。

灵感来自 answer by @Zlatko Karakaš to Use Inno Setup PreProcessor to get the files and size of the source path and its subdirs


更可靠(丑陋和复杂)的解决方案是使用用户定义的程序。这很复杂,因为预处理器缺乏对用户定义过程参数的支持。

[Files]

#define FindHandle
#define FindResult 
#dim InnerMask[65536]
#define InnerMask[0] ""

#sub ProcessFoundFile
    #define InnerFileName FindGetFileName(FindHandle)
    #define fileName InnerMask[InnerMaskWorkPosition] + InnerFileName
    #if InnerFileName!="." && InnerFileName!=".."
        #if DirExists(FileName)
            #define Public InnerMask[InnerMaskPosition] FileName+"\"
            #define Public InnerMaskPosition InnerMaskPosition + 1
        #else
            Source: {#FileName}; DestDir: {app}
        #endif
    #endif 
#endsub

#sub ProcessInnerMaskPosition 
    #for { \
        FindHandle = FindResult = \
            FindFirst(InnerMask[InnerMaskWorkPosition]+"*", faAnyFile); \
        FindResult; FindResult = FindNext(FindHandle)} ProcessFoundFile
    #if FindHandle
        #expr FindClose(FindHandle)
    #endif
#endsub

#sub CollectFiles
    #define Public InnerMaskPosition 1
    #define Public InnerMaskWorkPosition 0
    #for { \
        InnerMaskWorkPosition = 0; InnerMaskWorkPosition < InnerMaskPosition; \
            InnerMaskWorkPosition++} \
            ProcessInnerMaskPosition
    #undef Public InnerMaskPosition
    #undef Public InnerMaskWorkPosition
#endsub

#expr InnerMask[0]="C:\kh25\dependencies\"
#expr CollectFiles

取自answer by @René Martin to Use Inno Setup PreProcessor to get the files and size of the source path and its subdirs.

的递归扫描函数

在 Inno Setup 脚本的 末尾 添加一个 SaveToFile 调用也可以看到,预处理器生成的内容:

#expr SaveToFile(AddBackslash(SourcePath) + "Preprocessed.iss")

Inno Setup: How do I see the output (translation) of the Inno Setup Preprocessor?


我对这个问题的回答解释了上述两种方法之间的区别: