Circular package dependencies

A simple fix is suggested by your humble editor, and a slicker one by Brent Hartwig. Last Updated: 2006-10-24

=N.N. asks=

I've had a recurring problem with requiring a file. It gives an odd message about a function definition not being available in a completely different file, but it goes away if I take out the require. My guess is that there is a problem with a circular require, or something similar. Here is the main setup (consider all files to be in their own package of the same name):

File X  requires G   requires M <- This line causes the error File M  requires D   requires G   requires E File F   requires X   requires G

The error states that a call in File F to a function in package X is not valid. If I remove the problem line, the error goes away.

In a prior similar problem it was 'source' that was the issue, not 'require'. I thought that by taking out the 'source' statements, this kind of referencing wouldn't be a problem, but apparently not.

Has anyone run into this? What is the basis for the issue, and moreover, how do I get rid of it?

=K J Kleist gives one option=

One solution to these kind of circular dependencies would be to create a package/file which contains empty dummy function definitions for the functions that trigger the error message:

package alphaDefines; function foo {} In another package, the real function is re-defined:
 * 1) untested code

package alphaOne; function foo(n) { ... }

Your main inititiation package contains:

package alphaInit; if (! require('alphaDefines')) { exit_editor(1); } if (! require('alphaOne')) { exit_editor(1); }

Note the use of the *function* require and the error checking. A failure here is likely because of a stupid speling error, and starting the editor would likely be pointless.

=Brent Hartwig offers a section option=

Instead of using require statements, I use autoload and source all files up front, only using require for Epic packages. The below is a chopped up version that I use. In packages that call other custom packages, change your require statements to autoload statements:

package uString; autoload uArray::response_array uArray;

Here's code from the first custom ACL that Epic reads...

if ( defined( INITIALIZED ) && INITIALIZED ) { return }; global _STRICT_; # function sourceAclFiles( aclFilenames[] ) {  local rtn = 1; # default to success local i;  local msg; for( i = 1; i <= count( aclFilenames ); i++ ) {     if ( execute( "source \"$aclFilenames[$i]\"" ) != 0 ) {        msg = "Unable to source \"$aclFilenames[$i]\""; if ( main::ERROR != "" ) {           msg .= "\n\n$main::ERROR"; }        msg .= "\n\nThe $APP_NAME Application is NOT operational." . \           "\n\nPlease contact Production Support before proceeding."; displayError( msg ); rtn = 0; break; }  }   return rtn; } # # function initPackage {  local i;   local msg; local initSuccessful = 0; # Require environment variables if ( checkEnvironmentVariables ) {     # Prepend custom ACL directories (not auto-sourced by Epic) local customAclDir = main::ENV[ "APTCUSTOM" ]. main::PCS. "scripts". \         main::PCS. "manual". main::PCS. "acl" append_load_path( customAclDir, 1 ); append_load_path( customAclDir . main::PCS . "utils", 1 ); # Source utility ACL and constants local aclFilenames[]; i = 0; aclFilenames[ ++i ] = "uArray"; aclFilenames[ ++i ] = "uString"; aclFilenames[ ++i ] = "uWindow"; aclFilenames[ ++i ] = "uDoc"; aclFilenames[ ++i ] = "uOid";              # After uDoc aclFilenames[ ++i ] = "uDate"; aclFilenames[ ++i ] = "uLog";              # After uDate aclFilenames[ ++i ] = "uFile"; aclFilenames[ ++i ] = "constants";         # Before non-utilities initSuccessful = sourceAclFiles( aclFilenames ); if ( initSuccessful ) {        # Source application ACL delete( aclFilenames ); i = 0; aclFilenames[ ++i ] = "xslcommon";         # Before intermediate aclFilenames[ ++i ] = "intermediate";      # Before buildbook aclFilenames[ ++i ] = "buildbook";         # After xslcommon aclFilenames[ ++i ] = "distiller"; aclFilenames[ ++i ] = "mergeFosi"; aclFilenames[ ++i ] = "externalProcess"; aclFilenames[ ++i ] = "keymap"; if ( DEBUG_LEVEL ) {           aclFilenames[ ++i ] = "devel"; }        initSuccessful = sourceAclFiles( aclFilenames ); }  }   global INITIALIZED = 1; } initPackage;
 * 1) Only allow source if not done already
 * 1) Require explicit variable declarations.
 * 1) Purpose: Attempt to source filenames provided by given array.
 * 2)          Require each one; post error message(s) if unable to
 * 3)          source one or more. Files may be fully qualified or
 * 4)          depend on the ACL load path. Array must be 1-based
 * 5)          (vs. associative) where the shelf value is the ACL
 * 6)          filename.
 * 1) Initialize this package, including...