4

我喜欢 NetHack,我想和源代码一起玩一些有趣的东西。在我这样做之前,我希望能够开箱即用地编译它,但我很难做到这一点。

我从这里下载了源代码,并按照这里的说明进行操作,但是没有用。

我最终得到以下

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,其中的说明似乎非常明确。但是,由于我没有更改任何内容,因此我不知道为什么它无法编译...

我认为自己是一个称职的程序员,但在 makefile 和将 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 文件?如果是这样,我在里面放什么?听起来像谷歌的问题......

编辑 3我发现是一个更旧的版本,并试图改变它,但它也没有工作......

编辑 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 帮助程序,所以现在我想下一步是找出原因?

编辑 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.owhich 是有意义的。

无论如何,$(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_TWINPFLAG没有工作。

最终(ish)编辑:原来罗斯里奇在他使用-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
4

1 回答 1

4

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

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

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

返回值

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

  • time 表示 UTC 1970 年 1 月 1 日午夜之前的日期。
  • 如果使用_ctime32or_wctime32和 time 表示 2038 年 1 月 19 日 03:14:07 之后的日期。
  • 如果使用_ctime64or_wctime64和 time 表示 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替代将是一个问题。在 Windowslong上只有 32 位,因此这意味着ctime正在传递一个数字,该数字是一部分时间值,一部分是随机位。在文档中继续阅读证实了这种情况,并为我们提供了一个可能的解决方案:

ctime是一个内联函数,其计算结果为_ctime64并且time_t等价于 __time64_t。如果需要强制编译器解释time_t为旧的 32 位 time_t,可以定义_USE_32BIT_TIME_T. 这样做会导致ctime评估为_ctime32. 不建议这样做,因为您的应用程序可能会在 2038 年 1 月 18 日之后失败,并且在 64 位平台上是不允许的

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

于 2014-08-06T00:51:24.567 回答