如何在 Windows 7 中编译 NetHack?

Posted

技术标签:

【中文标题】如何在 Windows 7 中编译 NetHack?【英文标题】:How do I compile NetHack in Windows 7? 【发布时间】:2014-09-27 10:15:19 【问题描述】:

我喜欢 NetHack,而且我想在源代码上找点乐子。 在我这样做之前,我希望能够开箱即用地编译它,但我很难做到这一点。

我从here 下载了源代码,并按照here 的说明进行操作,但没有成功。

我最终得到了以下内容

C:\nethack-3.4.3\src>mingw32-make -f Makefile.gcc install
creating directory o
gcc -c -mms-bitfields -I../include  -g -DWIN32CON -oo/makedefs.o ../util/makedefs.c
gcc -c -mms-bitfields -I../include  -g -DWIN32CON -DDLB   -oo/monst.o  ../src/monst.c
gcc -c -mms-bitfields -I../include  -g -DWIN32CON -DDLB   -oo/objects.o      ../src/objects.c
..\util\makedefs -v
Makefile.gcc:655: recipe for target '../include/date.h' failed
mingw32-make: *** [../include/date.h] Error -1073741819

我看了它所说的那句台词,但它并没有真正告诉我任何事情。我确实注意到在包含目录中创建的 date.h 文件总是空的,但这对我也没有多大帮助。我阅读了 Install.nt README,其中的说明似乎非常明确。但是,由于我没有更改任何内容,因此我不知道为什么它会无法编译...

我认为自己是一个称职的程序员,但我对生成文件和将 C 代码编译成可执行应用程序几乎一无所知,所以我很迷茫。我下载并安装了 MinGW...一切,我的意思是当我运行 MinGW 安装程序时,没有任何未卸载的东西。

我在这里做错了什么?

编辑:提到 date.h:

#
#  date.h should be remade every time any of the source or include
#  files is modified.
#

$(INCL)/date.h $(OPTIONS_FILE): $(U)makedefs.exe
    $(subst /,\,$(U)makedefs -v)

我确实注意到它似乎正在对OPTIONS_FILE 进行某种调用,这似乎已被注释掉。我将取消注释,看看会发生什么。

#$(OPTIONS_FILE): $(U)makedefs.exe
#$(subst /,\,$(U)makedefs -v)

编辑 2 没用。是否有可能我必须手动创建/更新 date.h 文件?如果是这样,我在里面放什么?听起来像是 Google 的问题...

EDIT 3我发现this 是一个更旧的版本,并尝试对其进行更改,但它也不起作用......

EDIT 4 有人提到Makedefs 这似乎是崩溃的事情。我发现似乎是导致问题的 C 函数:

void
do_date()

    long clocktim = 0;
    char *c, cbuf[60], buf[BUFSZ];
    const char *ul_sfx;

    filename[0]='\0';
#ifdef FILE_PREFIX
    Strcat(filename,file_prefix);
#endif
    Sprintf(eos(filename), INCLUDE_TEMPLATE, DATE_FILE);
    if (!(ofp = fopen(filename, WRTMODE))) 
        perror(filename);
        exit(EXIT_FAILURE);
    
    Fprintf(ofp,"/*\tSCCS Id: @(#)date.h\t3.4\t2002/02/03 */\n\n");
    Fprintf(ofp,Dont_Edit_Code);

#ifdef KR1ED
    (void) time(&clocktim);
    Strcpy(cbuf, ctime(&clocktim));
#else
    (void) time((time_t *)&clocktim);
    Strcpy(cbuf, ctime((time_t *)&clocktim));
#endif
    for (c = cbuf; *c; c++) if (*c == '\n') break;
    *c = '\0';  /* strip off the '\n' */
    Fprintf(ofp,"#define BUILD_DATE \"%s\"\n", cbuf);
    Fprintf(ofp,"#define BUILD_TIME (%ldL)\n", clocktim);
    Fprintf(ofp,"\n");
#ifdef NHSTDC
    ul_sfx = "UL";
#else
    ul_sfx = "L";
#endif
    Fprintf(ofp,"#define VERSION_NUMBER 0x%08lx%s\n",
        version.incarnation, ul_sfx);
    Fprintf(ofp,"#define VERSION_FEATURES 0x%08lx%s\n",
        version.feature_set, ul_sfx);
#ifdef IGNORED_FEATURES
    Fprintf(ofp,"#define IGNORED_FEATURES 0x%08lx%s\n",
        (unsigned long) IGNORED_FEATURES, ul_sfx);
#endif
    Fprintf(ofp,"#define VERSION_SANITY1 0x%08lx%s\n",
        version.entity_count, ul_sfx);
    Fprintf(ofp,"#define VERSION_SANITY2 0x%08lx%s\n",
        version.struct_sizes, ul_sfx);
    Fprintf(ofp,"\n");
    Fprintf(ofp,"#define VERSION_STRING \"%s\"\n", version_string(buf));
    Fprintf(ofp,"#define VERSION_ID \\\n \"%s\"\n",
        version_id_string(buf, cbuf));
    Fprintf(ofp,"\n");
#ifdef AMIGA
    
    struct tm *tm = localtime((time_t *) &clocktim);
    Fprintf(ofp,"#define AMIGA_VERSION_STRING ");
    Fprintf(ofp,"\"\\0$VER: NetHack %d.%d.%d (%d.%d.%d)\"\n",
        VERSION_MAJOR, VERSION_MINOR, PATCHLEVEL,
        tm->tm_mday, tm->tm_mon+1, tm->tm_year+1900);
    
#endif
    Fclose(ofp);
    return;

另外我应该提到,当它在编译过程中到达这一点时,立即有这个图像:

所以我们已经将问题(我认为?)缩小到破坏事物的 makedefs 帮助程序,所以现在我想下一步是找出原因?

EDIT 5:建议在编译 Makedefs.c 时使用特殊参数。我查看了 Makefile 以找出编译发生的位置,我想我已经找到了发生的位置,但我真的不知道这里发生了什么。

$(U)makedefs.exe: $(MAKEOBJS)
    @$(link) $(LFLAGSU) -o$@ $(MAKEOBJS)

$(O)makedefs.o: $(CONFIG_H) $(INCL)/monattk.h $(INCL)/monflag.h \
     $(INCL)/objclass.h $(INCL)/monsym.h $(INCL)/qtext.h \
     $(INCL)/patchlevel.h $(U)makedefs.c $(O)obj.tag
    $(cc) $(CFLAGSU) -o$@ $(U)makedefs.c

我知道$(*) 是一个变量或Makefile 等效于一个变量。

$(U) 指向$(UTIL)/$(UTIL) 指向../util$(MAKEOBJS) 指向 $(O)makedefs.o $(O)monst.o $(O)objects.o$(O) 指向 $(OBJ)/ 指向 o 以便 $(O)makedefs.oo/makedefs.o 相同,考虑到我在半成功运行中观察到的行为,这是有道理的(在大冻结)。

无论如何,$(link) 指向gcc$(LFLAGSU) 指向 $(LFLAGSBASEC) 指向 $(linkdebug) 指向 -g

$(CONFIG_H)指向大量头文件:

CONFIG_H = $(INCL)/config.h $(INCL)/config1.h $(INCL)/tradstdc.h \
           $(INCL)/global.h $(INCL)/coord.h $(INCL)/vmsconf.h \
           $(INCL)/system.h $(INCL)/unixconf.h $(INCL)/os2conf.h \
           $(INCL)/micro.h $(INCL)/pcconf.h $(INCL)/tosconf.h \
           $(INCL)/amiconf.h $(INCL)/macconf.h $(INCL)/beconf.h \
           $(INCL)/ntconf.h $(INCL)/nhlan.h

$(INCL) 指向../include$(CFLAGSU) 指向 $(CFLAGSBASE) $(WINPFLAG)$(CFLAGSBASE) 指向 -c $(cflags) -I$(INCL) $(WINPINC) $(cdebug) $(cflags) 指向 -mms-bitfields $(WINPINC) 指向 -I$(WIN32) $(WIN32) 指向../win/win32 $(cdebug) 指向 -g $(WINPFLAG) 指向 -DTILES -DMSWIN_GRAPHICS -D_WIN32_IE=0x0400 。 . . 它就在那里。我认为这就是我需要修改的内容,以使其与 RossRidge -D_USE_32BIT_TIME_T 提到的内容一起工作。

但是,既然我已经走了这么远,我确实想知道其中一些东西的含义。 查看第一行时,我看到$(U)makedefs.exe :。对我来说,这似乎是编译输出文件的目标声明?那是对的吗?另外,@$(link) $(LFLAGSU) 之前和-o$ 之后的含义是什么?而-o后面的$是什么意思?

无论如何,我想尝试一下我想出的方法,看看它是否有效。 ... Aaaand 将 -D_USE_32BIT_TIME_T 添加到 WINPFLAG 不起作用。

最终(ish)编辑: 事实证明,RossRidge 使用 -D_USE_32BIT_TIME_T 标志的建议是正确的。我的错误是把它放在错误的地方。如果您查看框中的 Makefile.gcc,请查看第 165 行(在 IF 语句中)。你想在最后加上-D_USE_32BIT_TIME_T。但是您还需要在第 176 行的末尾添加它,该行位于该 IF 语句的 ELSE 末尾。所以整个块看起来像这样(不是一个巨大的变化,但如果你不这样做并且你在我的情况下运行,它仍然足以让它崩溃):

################################################
#                                              #
# Nothing below here should have to be changed.#
#                                              #
################################################

ifeq  "$(GRAPHICAL)" "Y"
WINPORT  = $(O)tile.o $(O)mhaskyn.o $(O)mhdlg.o \
    $(O)mhfont.o $(O)mhinput.o $(O)mhmain.o $(O)mhmap.o \
    $(O)mhmenu.o $(O)mhmsgwnd.o $(O)mhrip.o $(O)mhsplash.o \
    $(O)mhstatus.o $(O)mhtext.o $(O)mswproc.o $(O)winhack.o
WINPFLAG   = -DTILES -DMSWIN_GRAPHICS -D_WIN32_IE=0x0400 -D_USE_32BIT_TIME_T
NHRES   = $(O)winres.o
WINPINC = -I$(WIN32)
WINPHDR = $(WIN32)/mhaskyn.h $(WIN32)/mhdlg.h $(WIN32)/mhfont.h \
    $(WIN32)/mhinput.h $(WIN32)/mhmain.h $(WIN32)/mhmap.h \
    $(WIN32)/mhmenu.h $(WIN32)/mhmsg.h $(WIN32)/mhmsgwnd.h \
    $(WIN32)/mhrip.h $(WIN32)/mhstatus.h \
    $(WIN32)/mhtext.h $(WIN32)/resource.h $(WIN32)/winMS.h
WINPLIBS =  -lcomctl32 -lwinmm
else
WINPORT = $(O)nttty.o
WINPFLAG= -DWIN32CON -D_USE_32BIT_TIME_T
WINPHDR =
NHRES   = $(O)console.o
WINPINC =
WINPLIBS = -lwinmm
endif

【问题讨论】:

查找 date.h 将是一个好的开始:) 问题出现在Makefile.gcc 的第 655 行,所以看看那里,看看你是否可以弄清楚 makefile 在那个时候试图做什么以及为什么它可能会失败。 date.h 由 makedefs 生成 @MartinJames:我的阅读是在 makefile 尝试 create date.h 时发生错误。 你有util/makedefs.exe吗?当您手动运行makedefs -v 时会发生什么? 【参考方案1】:

(我不知道我的答案是否值得称赞,因为如果没有 Harry Johnston 和 indiv 的 cmets,我不会知道问题出在哪里,但我会尝试将 cmets 扩展到完整的回答。)

正如indiv 解释的那样,makedefs.exe 崩溃的原因是因为ctime 返回 NULL。通常你不会期望ctime 这样做,所以我们需要查看文档了解在什么情况下它会返回错误。由于使用 MinGW 进行编译,我们需要查看 Microsoft 的 Visual C++ 文档。这是因为 MinGW 没有自己的 C 运行时,它只是使用 Microsoft 的。

查看 ctime 的 Visual Studio C 运行时库参考条目,我们发现:

返回值

指向字符串结果的指针。 NULL 将在以下情况下返回:

时间表示 UTC 1970 年 1 月 1 日午夜之前的日期。 如果您使用 _ctime32_wctime32 并且时间表示 2038 年 1 月 19 日 03:14:07 之后的日期。 如果您使用_ctime64_wctime64 并且时间表示UTC 3000 年12 月31 日23:59:59 之后的日期。

现在可以假设原始发布者没有将他的系统时钟设置为遥远的未来或很久以前的时间。那么为什么ctime 会使用错误的时间呢? Harry Johnston 指出代码使用long 而不是time_t 来存储时间值。这并不奇怪。 Nethack 是真正的旧代码,最初 Unix 将其时间存储在 long 值中,后来使用 time_t 存储时间值。 Nethack 在其活跃的开发阶段的大部分时间里都必须处理没有time_t 的旧系统。

这解释了为什么 Nethack 源使用了错误的类型,但并不能完全解释为什么它将错误的值传递给 ctime。事实上我们没有看到ctime 本身的返回值的描述,只有_ctime32_ctime64 给了我们一个线索。如果 time_t 是 64 位类型,那么使用 long 代替将是一个问题。在 Windows 上,long 只有 32 位,因此这意味着 ctime 正在传递一个数字,该数字是一部分时间值,一部分是随机位。阅读文档证实了这种情况,并为我们提供了一个可能的解决方案:

ctime 是一个内联函数,计算结果为_ctime64time_t 等价于 __time64_t。如果您需要强制编译器将 time_t 解释为旧的 32 位 time_t,你可以定义_USE_32BIT_TIME_T。这样做会导致ctime 评估 到_ctime32。不建议这样做,因为您的申请可能在一月之后失败 18, 2038, 64位平台不允许

现在,由于定义 _USE_32BIT_TIME_T 只会影响 C 头文件的编译方式,并且由于 MinGW 提供它自己的 C 头文件,因此 MinGW 可能不支持这一点。快速检查 MinGW 的 time.h 会发现确实如此,因此使用 -D_USE_32BIT_TIME_T 编译器选项定义此宏的简单解决方案。

【讨论】:

对该问题的出色描述,+1。一个可能值得扩展的潜在复杂性,尽管在这种情况下它被证明不是问题:Windows 附带的 C 运行时 (msvcrt.dll) 没有正式文档,因为第三方应用程序实际上不应该不再使用它。最接近的是 Visual Studio 6 的文档,因为在 VS6 中编译的应用程序确实使用 msvcrt.dll,并且它们需要保持运行以实现向后兼容性。但是 VS6 文档忽略了所有提及 _ctime32_ctime64 并且没有指定 time_t 的大小。 ...所以,我不清楚当前 VS 文档中给出的详细信息对于 msvcrt.dll 是否准确。正如您所描述的,此信息的另一个来源是 MinGW 标头;这些可能是基于来自 VS6 标头的信息,并根据需要结合试错和/或逆向工程。 (当然,这些特定功能还存在一些风险,即没有仔细研究这些特定功能,并且标头会不准确,但显然情况并非如此。据我所知,它们通常非常可靠。 ) 至少在官方上,MinGW 标头仅基于 Microsoft 的公共文档。 Microsoft 为每个操作系统版本更新MSVCRT.DLL,因此它获得了旧的 VS6 DLL 没有的功能,例如 64 位时间值。 MinGW 的time.h 曾经有允许与旧的 VS6 DLL 兼容的版本宏,但现在它们似乎被破坏了。目前,MinGW 似乎假定为 MSCVRT.DLL 的较新版本之一 啊,这有助于解释为什么 msvcrt.dll 似乎导出 ctime 以及 _ctime32_ctime64。大概后两者被 Windows 本身(和 MinGW)使用,前者是为了 VS6 兼容性。 (在更新标头时仍然必须进行一些猜测,如果没有其他猜测给定操作系统的运行时基于哪个版本的 Visual Studio。):-)

以上是关于如何在 Windows 7 中编译 NetHack?的主要内容,如果未能解决你的问题,请参考以下文章

无法使用 MinGW 编译先前修改的 Nethack - 缓存问题?

如何从 Node.js 连接到 nethack?

NetHack 走廊实施

NetHack 源代码中的“NEARDATA”是啥意思?

如何在 Windows 7 中编译 C 编程? [关闭]

Nethack 源代码在哪里[关闭]