最近在一个新机子上用Spack
配置好编译器, hdf5, netcdf-c, netcdf-fortran, jasper等一系列依赖库后编译了最新版本的WRF-4.4.2
和WPS-4.4
WRF没问题, WPS-4.4
这边的ungrib.exe
怎么都不行, 报如下错:
./ngl/libg2_4.a(dec_jpeg2000.o): In function `dec_jpeg2000_':
dec_jpeg2000.c:(.text+0x61): undefined reference to `jpc_decode'
collect2: error: ld returned 1 exit status
make[1]: [Makefile:19: ungrib.exe] Error 1 (ignored)
但是很奇怪的是, 我用nm
看libjasper.so
里是有jpc_decode
这个函数的, LD_LIBRARY_PATH
, LIBRARY_PATH
之类的改了半天, 也都没有用, 最后研究了小半天才解决, 这里记录一下
Update @2023-03-28 16:33:34
mdzz, 结果按下面编译出来跑起来又报错了
jas_init.c:919: jas_get_ctx_internal: Assertion `ctx' failed.
不搞了, 老老实实切回了jasper-1.900.1
, 淦!
省流
解决方案(选一):
- 装个老版本的
jasper
- 修改
jasper
源码, 手动编译, 暴露jpc_decode
函数 - 修改ungrib里的代码, 将
jpc_decode
替换为jas_image_decode
其实装个老版本应该就行了, 但是由于实在奇怪, 我还是搞清楚选择了修改ungrib的源码....
Bug分析
首先通过Google发现这么一个帖子 :
https://github.com/NOAA-EMC/NCEPLIBS-g2c/issues/245
提到jpc_decode
现在是个内部函数, 开发者希望改为用jas_image_decode
从include/jasper/jas_image.h
中可以看到:
#if defined(JAS_INCLUDE_JPC_CODEC)
/* Format-dependent operations for JPEG-2000 code stream support. */
//JAS_EXPORT
jas_image_t *jpc_decode(jas_stream_t *in, const char *optstr);
//JAS_EXPORT
int jpc_encode(jas_image_t *image, jas_stream_t *out, const char *optstr);
//JAS_EXPORT
int jpc_validate(jas_stream_t *in);
#endif
然后这个JAS_INCLUDE_JPC_CODEC
是在include/jasper/jas_config.h
里define
过了的:
#define JAS_INCLUDE_JPC_CODEC
但是呢, 和jas_image_decode
相比, jpc_decode
缺少这个东西 : JAS_EXPORT
JAS_EXPORT
jas_image_t *jas_image_decode(jas_stream_t *in, int fmt, const char *optstr);
JAS_EXPORT
是在include/jasper/jas_export_cmake.h
中定义的:
# define JAS_EXPORT __attribute__((visibility("default")))
而visibility的影响参考这里提到的:
而编译时使用internal和hidden选项时,如果函数内没有:__attribute ((visibility("default")))声明,动态库使隐藏的不可被外界调用。
由于我是用spack
配置环境的, 进到这里后(关键是最后的.spack
):
opt/spack/linux-centos7-zen/gcc-7.5.0/jasper-3.0.3-zliofrszjxt7h7xjmjnn3scbccxkyq7k/.spack
grep可以发现这样的编译指令 : -fvisibility=hidden
ok, 所以其实jpc_decode
在动态库中实际上是不可见的
因此, 理论上修改源码或者更改编译指令后, 重新编译即可, 但是我是spack编译的, 懒得搞的
那么就去修改ungrib
里的源码, 找到ungrib/src/ngl/g2/dec_jpeg2000.c
里出错的行:
// jas_image_t *jpc_decode(jas_stream_t *in, const char *optstr)
image=jpc_decode(jpcstream,opts);
改为:
// jas_image_t *jas_image_decode(jas_stream_t *in, int fmt, const char *optstr)
image=jas_image_decode(jpcstream,4,opts);
这里解释一下参数, 首先从declaration上看, 就是中间多了一个指定格式的int参数 : fmt
然后这里在jasper的源码里的src/libjasper/base/jas_init.c
里看到一个数组jas_image_fmts
, 里面jpeg-2000(jpc)
的index是4
, 所以这里改成4
然后就编译出来了
总结
之前都是手动编译的, jasper都用的巨老的版本, 从来没遇到过这个问题, 这次用spack
配置, 爽是爽, 下次考虑酌情选择老一点的版本, 不选的话默认就是最新的, 是真的容易出问题... ...