@profoundlogic/codermake 1.7.0 → 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,7 +218,7 @@ 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
@@ -208,21 +226,21 @@ endef
208
226
  # Build ILE COBOL program (bound)
209
227
  define BUILD_CBLLE_PGM
210
228
  $(call SETUP_LOG,$(notdir $@))
211
- $(call REMOTE_BUILD,$(filter-out build/%,$^),system "crtbndcbl pgm($(LIBRARY)/$(basename $(@F))) srcstmf('$<') output(*print) dbgview(*all)")
229
+ $(call REMOTE_BUILD,$(filter-out build/%,$^),system "crtbndcbl pgm($(TARGET_LIB)/$(basename $(@F))) srcstmf('$<') tgtccsid(*job) output(*print) dbgview(*all)")
212
230
  touch '$@'
213
231
  endef
214
232
 
215
233
  # Build ILE COBOL module
216
234
  define BUILD_CBLLE_MODULE
217
235
  $(call SETUP_LOG,$(notdir $@))
218
- $(call REMOTE_BUILD,$(filter-out build/%,$^),system "crtcblmod module($(LIBRARY)/$(basename $(@F))) srcstmf('$<') output(*print) dbgview(*all)")
236
+ $(call REMOTE_BUILD,$(filter-out build/%,$^),system "crtcblmod module($(TARGET_LIB)/$(basename $(@F))) srcstmf('$<') tgtccsid(*job) output(*print) dbgview(*all)")
219
237
  touch '$@'
220
238
  endef
221
239
 
222
240
  # Build ILE CL program (bound)
223
241
  define BUILD_CLLE_PGM
224
242
  $(call SETUP_LOG,$(notdir $@))
225
- $(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)")
226
244
  touch '$@'
227
245
  endef
228
246
 
@@ -232,7 +250,7 @@ $(call SETUP_LOG,$(notdir $@))
232
250
  $(call CREATE_SOURCE_MEMBER,$<,qclsrc)
233
251
  $(SSH) $(QSHELL) <<'EOF'
234
252
  $(SET_LIBL)
235
- 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)"
236
254
  EOF
237
255
  touch '$@'
238
256
  endef
@@ -240,7 +258,7 @@ endef
240
258
  # Build ILE CL module
241
259
  define BUILD_CLLE_MODULE
242
260
  $(call SETUP_LOG,$(notdir $@))
243
- $(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)")
244
262
  touch '$@'
245
263
  endef
246
264
 
@@ -249,7 +267,7 @@ define BUILD_MODULE_TO_PGM
249
267
  $(call SETUP_LOG,$(notdir $@))
250
268
  $(SSH) $(QSHELL) <<'EOF'
251
269
  $(SET_LIBL)
252
- 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)"
253
271
  EOF
254
272
  touch '$@'
255
273
  endef
@@ -257,7 +275,7 @@ endef
257
275
  # Build service program from modules
258
276
  define BUILD_MODULE_TO_SRVPGM
259
277
  $(call SETUP_LOG,$(notdir $@))
260
- $(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)")
261
279
  touch '$@'
262
280
  endef
263
281
 
@@ -266,7 +284,7 @@ define BUILD_MODULE_TO_SRVPGM_ALL
266
284
  $(call SETUP_LOG,$(notdir $@))
267
285
  $(SSH) $(QSHELL) <<'EOF'
268
286
  $(SET_LIBL)
269
- 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)"
270
288
  EOF
271
289
  touch '$@'
272
290
  endef
@@ -277,7 +295,7 @@ $(call SETUP_LOG,$(notdir $@))
277
295
  $(call CREATE_SOURCE_MEMBER,$<,qddssrc)
278
296
  $(SSH) $(QSHELL) <<'EOF'
279
297
  $(SET_LIBL)
280
- system "crtdspf file($(LIBRARY)/$(basename $(@F))) srcfile($(LIBRARY)/qddssrc)"
298
+ system "crtdspf file($(TARGET_LIB)/$(basename $(@F))) srcfile($(TARGET_LIB)/qddssrc)"
281
299
  EOF
282
300
  touch '$@'
283
301
  endef
@@ -289,7 +307,7 @@ $(call SETUP_LOG,$(notdir $@))
289
307
  $(call CREATE_SOURCE_MEMBER,tmp/$(basename $(@F)).dspf,qddssrc)
290
308
  $(SSH) $(QSHELL) <<'EOF'
291
309
  $(SET_LIBL)
292
- 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)"
293
311
  EOF
294
312
  rm -f 'tmp/$(basename $(@F)).dspf'
295
313
  touch '$@'
@@ -301,7 +319,7 @@ $(call SETUP_LOG,$(notdir $@))
301
319
  $(call CREATE_SOURCE_MEMBER,$<,qddssrc)
302
320
  $(SSH) $(QSHELL) <<'EOF'
303
321
  $(SET_LIBL)
304
- system "crtpf file($(LIBRARY)/$(basename $(@F))) srcfile($(LIBRARY)/qddssrc)"
322
+ system "crtpf file($(TARGET_LIB)/$(basename $(@F))) srcfile($(TARGET_LIB)/qddssrc)"
305
323
  EOF
306
324
  touch '$@'
307
325
  endef
@@ -312,7 +330,7 @@ $(call SETUP_LOG,$(notdir $@))
312
330
  $(call CREATE_SOURCE_MEMBER,$<,qddssrc)
313
331
  $(SSH) $(QSHELL) <<'EOF'
314
332
  $(SET_LIBL)
315
- system "crtlf file($(LIBRARY)/$(basename $(@F))) srcfile($(LIBRARY)/qddssrc)"
333
+ system "crtlf file($(TARGET_LIB)/$(basename $(@F))) srcfile($(TARGET_LIB)/qddssrc)"
316
334
  EOF
317
335
  touch '$@'
318
336
  endef
@@ -323,7 +341,7 @@ $(call SETUP_LOG,$(notdir $@))
323
341
  $(call CREATE_SOURCE_MEMBER,$<,qddssrc)
324
342
  $(SSH) $(QSHELL) <<'EOF'
325
343
  $(SET_LIBL)
326
- system "crtprtf file($(LIBRARY)/$(basename $(@F))) srcfile($(LIBRARY)/qddssrc)"
344
+ system "crtprtf file($(TARGET_LIB)/$(basename $(@F))) srcfile($(TARGET_LIB)/qddssrc)"
327
345
  EOF
328
346
  touch '$@'
329
347
  endef
@@ -331,28 +349,28 @@ endef
331
349
  # Build SQL table
332
350
  define BUILD_SQLTABLE_TO_FILE
333
351
  $(call SETUP_LOG,$(notdir $@))
334
- $(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))")
335
353
  touch '$@'
336
354
  endef
337
355
 
338
356
  # Build SQL index
339
357
  define BUILD_SQLINDEX_TO_FILE
340
358
  $(call SETUP_LOG,$(notdir $@))
341
- $(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))")
342
360
  touch '$@'
343
361
  endef
344
362
 
345
363
  # Build SQL view
346
364
  define BUILD_SQLVIEW_TO_FILE
347
365
  $(call SETUP_LOG,$(notdir $@))
348
- $(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))")
349
367
  touch '$@'
350
368
  endef
351
369
 
352
370
  # Build SQL procedure
353
371
  define BUILD_SQLPROC_TO_PGM
354
372
  $(call SETUP_LOG,$(notdir $@))
355
- $(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))")
356
374
  touch '$@'
357
375
  endef
358
376
 
@@ -375,13 +393,16 @@ define BUILD_MENU
375
393
  $(call SETUP_LOG,$(notdir $@))
376
394
  $(SSH) $(QSHELL) <<'EOF'
377
395
  $(SET_LIBL)
378
- 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)"
379
397
  EOF
380
398
  touch '$@'
381
399
  endef
382
400
 
383
401
  ######
384
- # 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.
385
406
  ######
386
407
  .PHONY: platform-clean
387
408
  platform-clean:
@@ -389,19 +410,39 @@ platform-clean:
389
410
  rm -rf tmp/logs
390
411
  rm -rf tmp/codermake_*.tar
391
412
  rm -rf tmp/rpg-pp-*
392
- @CLEAN_TARGETS="$(notdir $(TARGETS))"
393
- if [ -n "$$CLEAN_TARGETS" ]; then
413
+ @if [ -n "$(TARGETS)" ]; then
394
414
  $(SSH) $(QSHELL) <<EOF
395
- if [ -d $(LIBRARY_PATH) ]; then
396
- for pass in 1 2; do
397
- for target in $$CLEAN_TARGETS; do
398
- if [ "\$${target%.file}" != "\$$target" ]; then
399
- 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
400
441
  else
401
- rm -rf "$(LIBRARY_PATH)/\$$target"
442
+ rm -rf "/qsys.lib/\$$lib.lib/\$$obj" 2>/dev/null || true
402
443
  fi
403
- done
444
+ fi
404
445
  done
405
- fi
446
+ done
406
447
  EOF
407
448
  fi
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@profoundlogic/codermake",
3
- "version": "1.7.0",
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",
@@ -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:
@@ -186,7 +200,7 @@ existing.srvpgm: existing_mod.module newmod.module existing.exports
186
200
 
187
201
  ## Troubleshooting
188
202
 
189
- 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:
190
204
 
191
205
  1. Open the log file for the failing target.
192
206
  2. Search for error message IDs (e.g., `RNF`, `SQL`, `CPF`, `CPD`).
@@ -194,6 +208,12 @@ Build logs are in `tmp/logs/<target>.log`. When a build fails:
194
208
  4. For DDS/CL, look for `CPF` or `CPD` message IDs in the output.
195
209
  5. Use `codermake --print-rules` to verify the generated Makefile if the target is not being built at all.
196
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
+
197
217
  For detailed guidance on reading compile listings, see [references/compile-listings.md](references/compile-listings.md).
198
218
 
199
219
  ## Environment variables
@@ -204,7 +224,7 @@ codermake reads its configuration from environment variables (typically set in a
204
224
 
205
225
  | Variable | Description |
206
226
  |---|---|
207
- | `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) |
208
228
  | `IBMI_HOST` | IBM i hostname or IP address |
209
229
  | `IBMI_USER` | SSH username on the IBM i |
210
230
  | `IBMI_KEY` | Path to SSH private key (optional) |
@@ -213,4 +233,15 @@ codermake reads its configuration from environment variables (typically set in a
213
233
 
214
234
  | Variable | Description |
215
235
  |---|---|
216
- | `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).