@profoundlogic/codermake 1.6.3 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,14 +4,25 @@
4
4
  ######
5
5
 
6
6
  ######
7
- # Bail out if required environment variables are not set properly.
7
+ # Output library configuration. Two modes:
8
+ # - Single-library: IBMI_BUILD_LIBRARY required (today's behavior).
9
+ # - Multi-library: CODERMAKE_LIBRARY_MAP drives per-target output libraries.
10
+ # LIBRARY is intentionally undefined; recipes use TARGET_LIB
11
+ # (= $(or $(TARGET_LIBRARY),$(LIBRARY))) which resolves via
12
+ # the target-specific TARGET_LIBRARY emitted by the
13
+ # preprocessor for each output rule.
8
14
  ######
9
- ifeq (,$(IBMI_BUILD_LIBRARY))
10
- $(error Required variable IBMI_BUILD_LIBRARY is not set.)
11
- endif
12
- ifneq (1,$(words [$(IBMI_BUILD_LIBRARY)]))
13
- $(error IBMI_BUILD_LIBRARY variable is not set correctly. Set to a valid library name and try again.)
15
+ ifndef CODERMAKE_LIBRARY_MAP
16
+ ifeq (,$(IBMI_BUILD_LIBRARY))
17
+ $(error Required variable IBMI_BUILD_LIBRARY is not set.)
18
+ endif
19
+ ifneq (1,$(words [$(IBMI_BUILD_LIBRARY)]))
20
+ $(error IBMI_BUILD_LIBRARY variable is not set correctly. Set to a valid library name and try again.)
21
+ endif
22
+ LIBRARY := $(IBMI_BUILD_LIBRARY)
23
+ LIBRARY_PATH := /qsys.lib/$(LIBRARY).lib
14
24
  endif
25
+
15
26
  ifeq (,$(IBMI_HOST))
16
27
  $(warning Warning: Required variable IBMI_HOST is not set.)
17
28
  endif
@@ -20,10 +31,10 @@ ifeq (,$(IBMI_USER))
20
31
  endif
21
32
 
22
33
  ######
23
- # Output library.
34
+ # Resolved per-recipe library and library path.
24
35
  ######
25
- LIBRARY := $(IBMI_BUILD_LIBRARY)
26
- LIBRARY_PATH := /qsys.lib/$(LIBRARY).lib
36
+ TARGET_LIB = $(or $(TARGET_LIBRARY),$(LIBRARY))
37
+ TARGET_LIB_PATH = /qsys.lib/$(TARGET_LIB).lib
27
38
 
28
39
  ######
29
40
  # Shell configuration.
@@ -58,14 +69,14 @@ SSH := $(SSH) $(IBMI_USER)@$(IBMI_HOST)
58
69
  ######
59
70
  define CREATE_SOURCE_MEMBER
60
71
  $(SSH) $(QSHELL) <<'EOF'
61
- if [ ! -e '$(LIBRARY_PATH)/$(2).file' ]; then
62
- system "crtsrcpf file($(LIBRARY)/$(2))"
72
+ if [ ! -e '$(TARGET_LIB_PATH)/$(2).file' ]; then
73
+ system "crtsrcpf file($(TARGET_LIB)/$(2))"
63
74
  fi
64
- if [ ! -e '$(LIBRARY_PATH)/$(2).file/$(basename $(notdir $(1))).mbr' ]; then
65
- system "addpfm file($(LIBRARY)/$(2)) mbr($(basename $(notdir $(1)))) srctype($(subst .,,$(suffix $(1))))"
75
+ if [ ! -e '$(TARGET_LIB_PATH)/$(2).file/$(basename $(notdir $(1))).mbr' ]; then
76
+ system "addpfm file($(TARGET_LIB)/$(2)) mbr($(basename $(notdir $(1)))) srctype($(subst .,,$(suffix $(1))))"
66
77
  fi
67
78
  EOF
68
- cat '$(1)' | $(SSH) "$(QSHELL) -c 'Rfile -Qw \"$(LIBRARY)/$(2)($(basename $(notdir $(1))))\"'"
79
+ cat '$(1)' | $(SSH) "$(QSHELL) -c 'Rfile -Qw \"$(TARGET_LIB)/$(2)($(basename $(notdir $(1))))\"'"
69
80
  endef
70
81
 
71
82
  ######
@@ -75,40 +86,30 @@ endef
75
86
  ######
76
87
  define BUILD_FROM_CL_FILE
77
88
  $(call REMOTE_BUILD,$(2), \
78
- rm -rf '$(LIBRARY_PATH)/$(notdir $(1))' && \
79
- export LIBRARY=$(LIBRARY) && \
89
+ rm -rf '$(TARGET_LIB_PATH)/$(notdir $(1))' && \
90
+ export LIBRARY=$(TARGET_LIB) && \
80
91
  export NAME=$(basename $(notdir $(1))) && \
81
92
  ( \
82
93
  echo "$(file < $<)" | while IFS= read -r line; do \
83
94
  system "$$line"; \
84
95
  done \
85
- ) || (rm -rf '$(LIBRARY_PATH)/$(notdir $(1))'; exit 1) \
96
+ ) || (rm -rf '$(TARGET_LIB_PATH)/$(notdir $(1))'; exit 1) \
86
97
  )
87
98
  endef
88
99
 
89
100
  ######
90
101
  # Utility to output "Creating X..." message and redirect recipe output to log file.
102
+ # In multi-library mode, logs nest under the target's library to avoid collisions.
91
103
  ######
104
+ LOG_DIR = tmp/logs$(if $(TARGET_LIBRARY),/$(TARGET_LIBRARY))
92
105
  define SETUP_LOG
93
106
  @echo "Creating $(1)"
94
- exec > 'tmp/logs/$(1).log' 2>&1
107
+ $(MKDIR) -p '$(LOG_DIR)'
108
+ exec > '$(LOG_DIR)/$(1).log' 2>&1
95
109
  endef
96
110
 
97
111
  ######
98
112
  # Reusable utility for remote builds via SSH.
99
- # Handles the complete pattern of:
100
- # 1. Creating an uncompressed tar archive of source files locally (preserving paths)
101
- # 2. Creating a temporary directory on IBM i and uploading the tar
102
- # 3. Extracting the tar (recreating directory structure), then executing build command with library list set
103
- # 4. Cleaning up temp directories (local and remote) and propagating exit codes
104
- #
105
- # $(1) = space-separated list of local files to upload
106
- # $(2) = build command to execute (typically a 'system' command)
107
- #
108
- # Files are archived with their full paths, so 'src/myfile.rpgle' will extract to
109
- # '$$REMOTE_DIR/src/myfile.rpgle' on the remote system.
110
- #
111
- # Build command runs with current directory set to the remote temp directory.
112
113
  ######
113
114
  ######
114
115
  # $(1) = source root directory to tar from (use '.' for current directory)
@@ -140,30 +141,47 @@ endef
140
141
 
141
142
  ######
142
143
  # Original REMOTE_BUILD is now a convenience wrapper (source root = '.')
143
- # $(1) = space-separated list of local files to upload
144
- # $(2) = build command to execute
145
144
  ######
146
145
  define REMOTE_BUILD
147
146
  $(call REMOTE_BUILD_FROM,.,$(1),$(2))
148
147
  endef
149
148
 
150
149
  ######
151
- # Prerun actions.
150
+ # Prerun actions and banner.
152
151
  ######
153
152
  $(info ===)
154
153
  $(info Target IBM i: $(IBMI_HOST))
155
- $(info Target library: $(LIBRARY))
154
+ ifdef CODERMAKE_LIBRARY_MAP
155
+ $(info Library map: $(CODERMAKE_RESOLVED_LIBRARY_MAP))
156
+ ifdef CODERMAKE_LIBRARY_LIST
157
+ $(info Library list: $(CODERMAKE_LIBRARY_LIST))
158
+ else
159
+ $(info Library list: (inherited from job/profile))
160
+ endif
161
+ else
162
+ $(info Target library: $(LIBRARY))
163
+ endif
156
164
  $(info Build user: $(IBMI_USER))
157
165
  $(info ===)
158
166
  $(shell mkdir -p build)
159
167
 
160
168
  ######
161
- # Library creation target
169
+ # Library creation. Single-library mode keeps today's specific-target file
170
+ # marker (`build/<LIBRARY>.lib` is a regular touched file). Multi-library mode
171
+ # uses a pattern rule where the directory itself (`build/<lib>.lib/`) is the
172
+ # library-existence marker — created by the recipe alongside the remote crtlib.
162
173
  ######
174
+ ifndef CODERMAKE_LIBRARY_MAP
163
175
  build/$(LIBRARY).lib:
164
176
  $(call SETUP_LOG,$(notdir $@))
165
177
  $(SSH) "test -e $(LIBRARY_PATH) || system \"crtlib $(LIBRARY) *test aut(*use) crtaut(*use)\""
166
178
  touch '$@'
179
+ else
180
+ build/%.lib:
181
+ $(call SETUP_LOG,$(notdir $@))
182
+ $(SSH) "test -e /qsys.lib/$*.lib || system \"crtlib $* *test aut(*use) crtaut(*use)\""
183
+ mkdir -p '$@'
184
+ endif
167
185
 
168
186
  ######
169
187
  # Recipe utilities (callable functions for preprocessor-generated rules)
@@ -173,7 +191,7 @@ build/$(LIBRARY).lib:
173
191
  define BUILD_RPGLE_PGM
174
192
  $(call SETUP_LOG,$(notdir $@))
175
193
  $(call RPG_PREPROCESS_SOURCES,$(filter-out build/%,$^))
176
- $(call REMOTE_BUILD_FROM,$$RPG_SRC_ROOT,$(filter-out build/%,$^),system "crtbndrpg pgm($(LIBRARY)/$(basename $(@F))) srcstmf('$<') incdir('.') tgtccsid(*job) output(*print) dbgview(*all)")
194
+ $(call REMOTE_BUILD_FROM,$$RPG_SRC_ROOT,$(filter-out build/%,$^),system "crtbndrpg pgm($(TARGET_LIB)/$(basename $(@F))) srcstmf('$<') incdir('.') tgtccsid(*job) output(*print) dbgview(*all)")
177
195
  $(call RPG_CLEANUP_SOURCES)
178
196
  touch '$@'
179
197
  endef
@@ -182,7 +200,7 @@ endef
182
200
  define BUILD_SQLRPGLE_PGM
183
201
  $(call SETUP_LOG,$(notdir $@))
184
202
  $(call RPG_PREPROCESS_SOURCES,$(filter-out build/%,$^))
185
- $(call REMOTE_BUILD_FROM,$$RPG_SRC_ROOT,$(filter-out build/%,$^),system "crtsqlrpgi obj($(LIBRARY)/$(basename $(@F))) objtype(*pgm) srcstmf('$<') cvtccsid(*job) output(*print) rpgppopt(*lvl2) compileopt('incdir(''.'') tgtccsid(*job) output(*print)') dbgview(*source)")
203
+ $(call REMOTE_BUILD_FROM,$$RPG_SRC_ROOT,$(filter-out build/%,$^),system "crtsqlrpgi obj($(TARGET_LIB)/$(basename $(@F))) objtype(*pgm) srcstmf('$<') cvtccsid(*job) output(*print) rpgppopt(*lvl2) compileopt('incdir(''.'') tgtccsid(*job) output(*print)') dbgview(*source)")
186
204
  $(call RPG_CLEANUP_SOURCES)
187
205
  touch '$@'
188
206
  endef
@@ -191,7 +209,7 @@ endef
191
209
  define BUILD_RPGLE_MODULE
192
210
  $(call SETUP_LOG,$(notdir $@))
193
211
  $(call RPG_PREPROCESS_SOURCES,$(filter-out build/%,$^))
194
- $(call REMOTE_BUILD_FROM,$$RPG_SRC_ROOT,$(filter-out build/%,$^),system "crtrpgmod module($(LIBRARY)/$(basename $(@F))) srcstmf('$<') incdir('.') tgtccsid(*job) output(*print) dbgview(*all)")
212
+ $(call REMOTE_BUILD_FROM,$$RPG_SRC_ROOT,$(filter-out build/%,$^),system "crtrpgmod module($(TARGET_LIB)/$(basename $(@F))) srcstmf('$<') incdir('.') tgtccsid(*job) output(*print) dbgview(*all)")
195
213
  $(call RPG_CLEANUP_SOURCES)
196
214
  touch '$@'
197
215
  endef
@@ -200,15 +218,29 @@ endef
200
218
  define BUILD_SQLRPGLE_MODULE
201
219
  $(call SETUP_LOG,$(notdir $@))
202
220
  $(call RPG_PREPROCESS_SOURCES,$(filter-out build/%,$^))
203
- $(call REMOTE_BUILD_FROM,$$RPG_SRC_ROOT,$(filter-out build/%,$^),system "crtsqlrpgi obj($(LIBRARY)/$(basename $(@F))) objtype(*module) srcstmf('$<') cvtccsid(*job) output(*print) rpgppopt(*lvl2) compileopt('incdir(''.'') tgtccsid(*job) output(*print)') dbgview(*source)")
221
+ $(call REMOTE_BUILD_FROM,$$RPG_SRC_ROOT,$(filter-out build/%,$^),system "crtsqlrpgi obj($(TARGET_LIB)/$(basename $(@F))) objtype(*module) srcstmf('$<') cvtccsid(*job) output(*print) rpgppopt(*lvl2) compileopt('incdir(''.'') tgtccsid(*job) output(*print)') dbgview(*source)")
204
222
  $(call RPG_CLEANUP_SOURCES)
205
223
  touch '$@'
206
224
  endef
207
225
 
226
+ # Build ILE COBOL program (bound)
227
+ define BUILD_CBLLE_PGM
228
+ $(call SETUP_LOG,$(notdir $@))
229
+ $(call REMOTE_BUILD,$(filter-out build/%,$^),system "crtbndcbl pgm($(TARGET_LIB)/$(basename $(@F))) srcstmf('$<') tgtccsid(*job) output(*print) dbgview(*all)")
230
+ touch '$@'
231
+ endef
232
+
233
+ # Build ILE COBOL module
234
+ define BUILD_CBLLE_MODULE
235
+ $(call SETUP_LOG,$(notdir $@))
236
+ $(call REMOTE_BUILD,$(filter-out build/%,$^),system "crtcblmod module($(TARGET_LIB)/$(basename $(@F))) srcstmf('$<') tgtccsid(*job) output(*print) dbgview(*all)")
237
+ touch '$@'
238
+ endef
239
+
208
240
  # Build ILE CL program (bound)
209
241
  define BUILD_CLLE_PGM
210
242
  $(call SETUP_LOG,$(notdir $@))
211
- $(call REMOTE_BUILD,$(filter-out build/%,$^),system "crtbndcl pgm($(LIBRARY)/$(basename $(@F))) srcstmf('$<') incdir('.') tgtccsid(*job) output(*print) dbgview(*all)")
243
+ $(call REMOTE_BUILD,$(filter-out build/%,$^),system "crtbndcl pgm($(TARGET_LIB)/$(basename $(@F))) srcstmf('$<') incdir('.') tgtccsid(*job) output(*print) dbgview(*all)")
212
244
  touch '$@'
213
245
  endef
214
246
 
@@ -218,7 +250,7 @@ $(call SETUP_LOG,$(notdir $@))
218
250
  $(call CREATE_SOURCE_MEMBER,$<,qclsrc)
219
251
  $(SSH) $(QSHELL) <<'EOF'
220
252
  $(SET_LIBL)
221
- system "crtclpgm pgm($(LIBRARY)/$(basename $(@F))) srcfile($(LIBRARY)/qclsrc) output(*print)"
253
+ system "crtclpgm pgm($(TARGET_LIB)/$(basename $(@F))) srcfile($(TARGET_LIB)/qclsrc) output(*print)"
222
254
  EOF
223
255
  touch '$@'
224
256
  endef
@@ -226,7 +258,7 @@ endef
226
258
  # Build ILE CL module
227
259
  define BUILD_CLLE_MODULE
228
260
  $(call SETUP_LOG,$(notdir $@))
229
- $(call REMOTE_BUILD,$(filter-out build/%,$^),system "crtclmod module($(LIBRARY)/$(basename $(@F))) srcstmf('$<') incdir('.') tgtccsid(*job) output(*print) dbgview(*all)")
261
+ $(call REMOTE_BUILD,$(filter-out build/%,$^),system "crtclmod module($(TARGET_LIB)/$(basename $(@F))) srcstmf('$<') incdir('.') tgtccsid(*job) output(*print) dbgview(*all)")
230
262
  touch '$@'
231
263
  endef
232
264
 
@@ -235,7 +267,7 @@ define BUILD_MODULE_TO_PGM
235
267
  $(call SETUP_LOG,$(notdir $@))
236
268
  $(SSH) $(QSHELL) <<'EOF'
237
269
  $(SET_LIBL)
238
- system "crtpgm pgm($(LIBRARY)/$(basename $(@F))) module($(foreach m,$(filter %.module,$^),$(basename $(notdir $(m))))) $(BNDSRVPGM_PARM) $(BNDDIR_PARM)"
270
+ system "crtpgm pgm($(TARGET_LIB)/$(basename $(@F))) $(MODULE_PARM) $(BNDSRVPGM_PARM) $(BNDDIR_PARM)"
239
271
  EOF
240
272
  touch '$@'
241
273
  endef
@@ -243,7 +275,7 @@ endef
243
275
  # Build service program from modules
244
276
  define BUILD_MODULE_TO_SRVPGM
245
277
  $(call SETUP_LOG,$(notdir $@))
246
- $(call REMOTE_BUILD,$(filter-out build/%,$^),system "crtsrvpgm srvpgm($(LIBRARY)/$(basename $(@F))) module($(foreach m,$(filter %.module,$^),$(basename $(notdir $(m))))) export(*srcfile) srcstmf('$(filter %.exports %.bnd,$^)') $(BNDSRVPGM_PARM) $(BNDDIR_PARM)")
278
+ $(call REMOTE_BUILD,$(filter-out build/%,$^),system "crtsrvpgm srvpgm($(TARGET_LIB)/$(basename $(@F))) $(MODULE_PARM) export(*srcfile) srcstmf('$(filter %.exports %.bnd,$^)') $(BNDSRVPGM_PARM) $(BNDDIR_PARM)")
247
279
  touch '$@'
248
280
  endef
249
281
 
@@ -252,7 +284,7 @@ define BUILD_MODULE_TO_SRVPGM_ALL
252
284
  $(call SETUP_LOG,$(notdir $@))
253
285
  $(SSH) $(QSHELL) <<'EOF'
254
286
  $(SET_LIBL)
255
- system "crtsrvpgm srvpgm($(LIBRARY)/$(basename $(@F))) module($(foreach m,$(filter %.module,$^),$(basename $(notdir $(m))))) export(*all) $(BNDSRVPGM_PARM) $(BNDDIR_PARM)"
287
+ system "crtsrvpgm srvpgm($(TARGET_LIB)/$(basename $(@F))) $(MODULE_PARM) export(*all) $(BNDSRVPGM_PARM) $(BNDDIR_PARM)"
256
288
  EOF
257
289
  touch '$@'
258
290
  endef
@@ -263,7 +295,7 @@ $(call SETUP_LOG,$(notdir $@))
263
295
  $(call CREATE_SOURCE_MEMBER,$<,qddssrc)
264
296
  $(SSH) $(QSHELL) <<'EOF'
265
297
  $(SET_LIBL)
266
- system "crtdspf file($(LIBRARY)/$(basename $(@F))) srcfile($(LIBRARY)/qddssrc)"
298
+ system "crtdspf file($(TARGET_LIB)/$(basename $(@F))) srcfile($(TARGET_LIB)/qddssrc)"
267
299
  EOF
268
300
  touch '$@'
269
301
  endef
@@ -275,7 +307,7 @@ $(call SETUP_LOG,$(notdir $@))
275
307
  $(call CREATE_SOURCE_MEMBER,tmp/$(basename $(@F)).dspf,qddssrc)
276
308
  $(SSH) $(QSHELL) <<'EOF'
277
309
  $(SET_LIBL)
278
- system "crtdspf file($(LIBRARY)/$(basename $(@F))) srcfile($(LIBRARY)/qddssrc) enhdsp(*yes)"
310
+ system "crtdspf file($(TARGET_LIB)/$(basename $(@F))) srcfile($(TARGET_LIB)/qddssrc) enhdsp(*yes)"
279
311
  EOF
280
312
  rm -f 'tmp/$(basename $(@F)).dspf'
281
313
  touch '$@'
@@ -287,7 +319,7 @@ $(call SETUP_LOG,$(notdir $@))
287
319
  $(call CREATE_SOURCE_MEMBER,$<,qddssrc)
288
320
  $(SSH) $(QSHELL) <<'EOF'
289
321
  $(SET_LIBL)
290
- system "crtpf file($(LIBRARY)/$(basename $(@F))) srcfile($(LIBRARY)/qddssrc)"
322
+ system "crtpf file($(TARGET_LIB)/$(basename $(@F))) srcfile($(TARGET_LIB)/qddssrc)"
291
323
  EOF
292
324
  touch '$@'
293
325
  endef
@@ -298,7 +330,7 @@ $(call SETUP_LOG,$(notdir $@))
298
330
  $(call CREATE_SOURCE_MEMBER,$<,qddssrc)
299
331
  $(SSH) $(QSHELL) <<'EOF'
300
332
  $(SET_LIBL)
301
- system "crtlf file($(LIBRARY)/$(basename $(@F))) srcfile($(LIBRARY)/qddssrc)"
333
+ system "crtlf file($(TARGET_LIB)/$(basename $(@F))) srcfile($(TARGET_LIB)/qddssrc)"
302
334
  EOF
303
335
  touch '$@'
304
336
  endef
@@ -309,7 +341,7 @@ $(call SETUP_LOG,$(notdir $@))
309
341
  $(call CREATE_SOURCE_MEMBER,$<,qddssrc)
310
342
  $(SSH) $(QSHELL) <<'EOF'
311
343
  $(SET_LIBL)
312
- system "crtprtf file($(LIBRARY)/$(basename $(@F))) srcfile($(LIBRARY)/qddssrc)"
344
+ system "crtprtf file($(TARGET_LIB)/$(basename $(@F))) srcfile($(TARGET_LIB)/qddssrc)"
313
345
  EOF
314
346
  touch '$@'
315
347
  endef
@@ -317,28 +349,28 @@ endef
317
349
  # Build SQL table
318
350
  define BUILD_SQLTABLE_TO_FILE
319
351
  $(call SETUP_LOG,$(notdir $@))
320
- $(call REMOTE_BUILD,$(filter-out build/%,$^),system "runsqlstm srcstmf('$<') commit(*none) dftrdbcol($(LIBRARY))")
352
+ $(call REMOTE_BUILD,$(filter-out build/%,$^),system "runsqlstm srcstmf('$<') commit(*none) dftrdbcol($(TARGET_LIB))")
321
353
  touch '$@'
322
354
  endef
323
355
 
324
356
  # Build SQL index
325
357
  define BUILD_SQLINDEX_TO_FILE
326
358
  $(call SETUP_LOG,$(notdir $@))
327
- $(call REMOTE_BUILD,$(filter-out build/%,$^),system "runsqlstm srcstmf('$<') commit(*none) dftrdbcol($(LIBRARY))")
359
+ $(call REMOTE_BUILD,$(filter-out build/%,$^),system "runsqlstm srcstmf('$<') commit(*none) dftrdbcol($(TARGET_LIB))")
328
360
  touch '$@'
329
361
  endef
330
362
 
331
363
  # Build SQL view
332
364
  define BUILD_SQLVIEW_TO_FILE
333
365
  $(call SETUP_LOG,$(notdir $@))
334
- $(call REMOTE_BUILD,$(filter-out build/%,$^),system "runsqlstm srcstmf('$<') commit(*none) dftrdbcol($(LIBRARY))")
366
+ $(call REMOTE_BUILD,$(filter-out build/%,$^),system "runsqlstm srcstmf('$<') commit(*none) dftrdbcol($(TARGET_LIB))")
335
367
  touch '$@'
336
368
  endef
337
369
 
338
370
  # Build SQL procedure
339
371
  define BUILD_SQLPROC_TO_PGM
340
372
  $(call SETUP_LOG,$(notdir $@))
341
- $(call REMOTE_BUILD,$(filter-out build/%,$^),system "runsqlstm srcstmf('$<') commit(*none) dftrdbcol($(LIBRARY))")
373
+ $(call REMOTE_BUILD,$(filter-out build/%,$^),system "runsqlstm srcstmf('$<') commit(*none) dftrdbcol($(TARGET_LIB))")
342
374
  touch '$@'
343
375
  endef
344
376
 
@@ -361,13 +393,16 @@ define BUILD_MENU
361
393
  $(call SETUP_LOG,$(notdir $@))
362
394
  $(SSH) $(QSHELL) <<'EOF'
363
395
  $(SET_LIBL)
364
- system "crtmnu menu($(LIBRARY)/$(basename $(@F))) type(*dspf) dspf($(basename $(notdir $(filter %.file,$^)))) msgf($(basename $(notdir $(filter %.msgf,$^)))) cmdlin(*none) dspkey(*no)"
396
+ system "crtmnu menu($(TARGET_LIB)/$(basename $(@F))) type(*dspf) dspf($(basename $(notdir $(filter %.file,$^)))) msgf($(basename $(notdir $(filter %.msgf,$^)))) cmdlin(*none) dspkey(*no)"
365
397
  EOF
366
398
  touch '$@'
367
399
  endef
368
400
 
369
401
  ######
370
- # Platform-specific clean
402
+ # Platform-specific clean. Each target's path encodes its library, so the
403
+ # library segment is extracted per-target. Works for both modes — in
404
+ # single-library mode every target path is `build/<obj>.<ext>` (no library
405
+ # segment), so we fall back to $(LIBRARY) for the IBM i delete.
371
406
  ######
372
407
  .PHONY: platform-clean
373
408
  platform-clean:
@@ -375,19 +410,39 @@ platform-clean:
375
410
  rm -rf tmp/logs
376
411
  rm -rf tmp/codermake_*.tar
377
412
  rm -rf tmp/rpg-pp-*
378
- @CLEAN_TARGETS="$(notdir $(TARGETS))"
379
- if [ -n "$$CLEAN_TARGETS" ]; then
413
+ @if [ -n "$(TARGETS)" ]; then
380
414
  $(SSH) $(QSHELL) <<EOF
381
- if [ -d $(LIBRARY_PATH) ]; then
382
- for pass in 1 2; do
383
- for target in $$CLEAN_TARGETS; do
384
- if [ "\$${target%.file}" != "\$$target" ]; then
385
- system "dltf $(LIBRARY)/\$${target%.file}" > /dev/null 2>&1 || true
415
+ # Two passes over all targets so dependent objects (e.g. SQL views/indexes
416
+ # over a table) get deleted in the first pass, freeing the underlying object
417
+ # for deletion in the second pass.
418
+ for pass in 1 2; do
419
+ for target in $(TARGETS); do
420
+ # Strip "build/" prefix to get the relative portion.
421
+ rel="\$${target#build/}"
422
+ if [ "\$$rel" = "\$$target" ]; then
423
+ continue
424
+ fi
425
+ # If "rel" contains a "/", it's "<lib>.lib/<obj>" (multi-library).
426
+ # Otherwise it's just "<obj>" (single-library, fall back to \$$LIBRARY).
427
+ case "\$$rel" in
428
+ */*)
429
+ lib_part="\$${rel%%/*}"
430
+ lib="\$${lib_part%.lib}"
431
+ obj="\$${rel#*/}"
432
+ ;;
433
+ *)
434
+ lib="$(LIBRARY)"
435
+ obj="\$$rel"
436
+ ;;
437
+ esac
438
+ if [ -d "/qsys.lib/\$$lib.lib" ]; then
439
+ if [ "\$${obj%.file}" != "\$$obj" ]; then
440
+ system "dltf \$$lib/\$${obj%.file}" > /dev/null 2>&1 || true
386
441
  else
387
- rm -rf "$(LIBRARY_PATH)/\$$target"
442
+ rm -rf "/qsys.lib/\$$lib.lib/\$$obj" 2>/dev/null || true
388
443
  fi
389
- done
444
+ fi
390
445
  done
391
- fi
446
+ done
392
447
  EOF
393
448
  fi
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@profoundlogic/codermake",
3
- "version": "1.6.3",
3
+ "version": "2.0.0",
4
4
  "description": "Make wrapper framework for IBM i development that simplifies build rules",
5
5
  "main": "dist/bin/index.js",
6
6
  "type": "module",
@@ -21,7 +21,8 @@
21
21
  "test:dist:integration": "CODERMAKE_E2E_TEST=true node --test --test-concurrency=1 dist/test/integration/**/*.test.js",
22
22
  "lint": "eslint . --ignore-pattern dist/",
23
23
  "start": "./bin/index.js",
24
- "prepublishOnly": "npm run lint && npm test && npm run build && npm run test:dist"
24
+ "check:multi-library-env": "node -e \"import('dotenv/config').then(() => { if (!process.env.CODERMAKE_E2E_LIBRARY_MAP || !process.env.CODERMAKE_E2E_LIBRARY_MAP.trim()) { console.error('CODERMAKE_E2E_LIBRARY_MAP must be set for prepublish (e.g. \\\"LIBA=DEVLIBA LIBB=DEVLIBB\\\"). Gates the multi-library output integration tests.'); process.exit(1); } })\"",
25
+ "prepublishOnly": "npm run check:multi-library-env && npm run lint && npm test && npm run build && npm run test:dist"
25
26
  },
26
27
  "author": "Profound Logic Software",
27
28
  "license": "SEE LICENSE IN LICENSE.txt",
@@ -5,7 +5,7 @@ description: Build IBM i objects from source code.
5
5
 
6
6
  # codermake
7
7
 
8
- codermake is a Make-based build tool for IBM i projects. It compiles RPG, CL, DDS, and SQL source into IBM i objects (programs, modules, service programs, files, menus, message files, binding directories) either locally on IBM i or remotely via SSH.
8
+ codermake is a Make-based build tool for IBM i projects. It compiles RPG, COBOL, CL, DDS, and SQL source into IBM i objects (programs, modules, service programs, files, menus, message files, binding directories) either locally on IBM i or remotely via SSH.
9
9
 
10
10
  ## Key constraints
11
11
 
@@ -76,6 +76,20 @@ Files with a path separator are treated as relative to the project root (not the
76
76
  mysrvpgm.srvpgm: mymod.module common/api.exports
77
77
  ```
78
78
 
79
+ In a multi-library project (sources organized under library-equivalent directories like `LIBA/qrpglesrc/`), two-segment paths whose first segment is a known library directory and whose second segment is a bare object filename with an output extension are recognized as **cross-library object qualifiers**:
80
+
81
+ ```makefile
82
+ # In LIBA/qrpglesrc/Rules.mk -- depend on a srvpgm in LIBB
83
+ crossbnd.pgm: crossbnd.rpgle | LIBB/utils.srvpgm
84
+
85
+ # Library-qualified target: source in LIBA's tree, output lands in LIBB
86
+ LIBB/installer.pgm: installer.rpgle
87
+ ```
88
+
89
+ Two-segment paths whose second segment has a *source* extension, or three-or-more-segment paths, are cross-library *source* passthroughs (e.g. `LIBB/qcpysrc/shared.rpgle`).
90
+
91
+ A library qualifier whose first segment is not a known library directory is an error — codermake will tell you which token failed validation.
92
+
79
93
  ### Message files and binding directories (dual-purpose pattern)
80
94
 
81
95
  The `.msgf` and `.bnddir` extensions serve double duty -- as source when they are the first prerequisite of a matching target type, and as built objects everywhere else:
@@ -135,6 +149,8 @@ ENDPGMEXP
135
149
  | `.rpgle` | `.module` | Module (*MODULE) | `CRTRPGMOD` |
136
150
  | `.sqlrpgle` | `.pgm` | Program (*PGM) | `CRTSQLRPGI OBJTYPE(*PGM)` |
137
151
  | `.sqlrpgle` | `.module` | Module (*MODULE) | `CRTSQLRPGI OBJTYPE(*MODULE)` |
152
+ | `.cblle` | `.pgm` | Program (*PGM) | `CRTBNDCBL` |
153
+ | `.cblle` | `.module` | Module (*MODULE) | `CRTCBLMOD` |
138
154
  | `.clle` | `.pgm` | Program (*PGM) | `CRTBNDCL` |
139
155
  | `.clle` | `.module` | Module (*MODULE) | `CRTCLMOD` |
140
156
  | `.clp` / `.cl` | `.pgm` | Program (*PGM) | `CRTCLPGM` |
@@ -184,7 +200,7 @@ existing.srvpgm: existing_mod.module newmod.module existing.exports
184
200
 
185
201
  ## Troubleshooting
186
202
 
187
- Build logs are in `tmp/logs/<target>.log`. When a build fails:
203
+ Build logs are in `tmp/logs/<target>.log`. In multi-library output mode (when `CODERMAKE_LIBRARY_MAP` is set) logs are nested under the IBM i library: `tmp/logs/<libraryName>/<target>.log`. When a build fails:
188
204
 
189
205
  1. Open the log file for the failing target.
190
206
  2. Search for error message IDs (e.g., `RNF`, `SQL`, `CPF`, `CPD`).
@@ -192,6 +208,12 @@ Build logs are in `tmp/logs/<target>.log`. When a build fails:
192
208
  4. For DDS/CL, look for `CPF` or `CPD` message IDs in the output.
193
209
  5. Use `codermake --print-rules` to verify the generated Makefile if the target is not being built at all.
194
210
 
211
+ Common errors:
212
+
213
+ - **`Invalid library qualifier '<lib>/<obj>.<ext>': '<lib>' is not a known library directory.`** — the first segment of a two-segment qualifier doesn't match any discovered library directory. Either fix the typo or add the missing directory to the project layout.
214
+ - **`<envvar> and CODERMAKE_LIBRARY_MAP are mutually exclusive.`** — only one output-mode variable can be set. Pick `BUILD_LIBRARY`/`IBMI_BUILD_LIBRARY` (single library) **or** `CODERMAKE_LIBRARY_MAP` (per-library output).
215
+ - **`CODERMAKE_LIBRARY_MAP is missing entries for: <dir1>, <dir2>`** — every discovered library directory must appear in an explicit map. Add the missing entries, or switch to `CODERMAKE_LIBRARY_MAP=auto` for natural mapping.
216
+
195
217
  For detailed guidance on reading compile listings, see [references/compile-listings.md](references/compile-listings.md).
196
218
 
197
219
  ## Environment variables
@@ -202,7 +224,7 @@ codermake reads its configuration from environment variables (typically set in a
202
224
 
203
225
  | Variable | Description |
204
226
  |---|---|
205
- | `IBMI_BUILD_LIBRARY` | Target IBM i library for compiled objects |
227
+ | `IBMI_BUILD_LIBRARY` | Target IBM i library for compiled objects (single-library output mode) |
206
228
  | `IBMI_HOST` | IBM i hostname or IP address |
207
229
  | `IBMI_USER` | SSH username on the IBM i |
208
230
  | `IBMI_KEY` | Path to SSH private key (optional) |
@@ -211,4 +233,15 @@ codermake reads its configuration from environment variables (typically set in a
211
233
 
212
234
  | Variable | Description |
213
235
  |---|---|
214
- | `BUILD_LIBRARY` | Target IBM i library for compiled objects |
236
+ | `BUILD_LIBRARY` | Target IBM i library for compiled objects (single-library output mode) |
237
+
238
+ ### Multi-library output (alternative to BUILD_LIBRARY / IBMI_BUILD_LIBRARY)
239
+
240
+ When the project has a multi-library layout, `CODERMAKE_LIBRARY_MAP` enables per-library output — each library-equivalent source directory builds into a distinct IBM i library. Mutually exclusive with `BUILD_LIBRARY` / `IBMI_BUILD_LIBRARY`.
241
+
242
+ | Variable | Description |
243
+ |---|---|
244
+ | `CODERMAKE_LIBRARY_MAP` | Either `auto` (natural mapping: dir name → library name) or explicit `LIBA=APPLIBA LIBB=APPLIBB` pairs. Mutually exclusive with `BUILD_LIBRARY` / `IBMI_BUILD_LIBRARY`. |
245
+ | `CODERMAKE_LIBRARY_LIST` | Optional. Space-separated, ordered IBM i library list. Replaces the user portion of the build job's library list. Only valid alongside `CODERMAKE_LIBRARY_MAP`. |
246
+
247
+ When using multi-library output, refer to objects in another library with the qualifier syntax `<libraryDir>/<bareobj>.<ext>` (see *Cross-directory references* above).
@@ -24,6 +24,16 @@ mypgm.pgm: mypgm.sqlrpgle
24
24
 
25
25
  **CL command:** `CRTSQLRPGI OBJ(LIB/MYPGM) OBJTYPE(*PGM) SRCSTMF('src/mypgm.sqlrpgle')`
26
26
 
27
+ ### ILE COBOL program from .cblle
28
+
29
+ Compiles an ILE COBOL source file into a bound program.
30
+
31
+ ```makefile
32
+ mypgm.pgm: mypgm.cblle
33
+ ```
34
+
35
+ **CL command:** `CRTBNDCBL PGM(LIB/MYPGM) SRCSTMF('src/mypgm.cblle')`
36
+
27
37
  ### ILE CL program from .clle
28
38
 
29
39
  Compiles an ILE CL source file into a bound program.
@@ -88,6 +98,14 @@ mymod.module: mymod.sqlrpgle
88
98
 
89
99
  **CL command:** `CRTSQLRPGI OBJ(LIB/MYMOD) OBJTYPE(*MODULE) SRCSTMF('src/mymod.sqlrpgle')`
90
100
 
101
+ ### ILE COBOL module from .cblle
102
+
103
+ ```makefile
104
+ mymod.module: mymod.cblle
105
+ ```
106
+
107
+ **CL command:** `CRTCBLMOD MODULE(LIB/MYMOD) SRCSTMF('src/mymod.cblle')`
108
+
91
109
  ### ILE CL module from .clle
92
110
 
93
111
  ```makefile