While investigating the various options for installing server-wide drush commands, I found some inconsistencies in the docs.

README.txt (commands section)

  - In a .drush folder in your HOME folder. Note, that you have
    to make the .drush folder yourself.
  - Along with one of your existing modules. If your command is
    related to an existing module, this is the preferred option.
  - In a folder specified with the include option (see above).
  - In /path/to/drush/commands (not a Smart Thing, but it would work).

drush_commandfile_list() documentation says:

 * - The ".drush" folder in the users HOME folder.
 * - The "/path/to/drush/includes" folder.
 * - Folders listed in the 'include' option (see example.drushrc.php).
 * - Active modules in the current Drupal installation (if any).

_drush_find_commandfiles() holds the truth:

      // Core commands shipping with drush
      $searchpath[] = realpath(dirname(__FILE__) . '/../commands/');

This reveals that the second line of documentation of drush_commandfile_list() is wrong.

      // User commands, specified by 'include' option
      if ($include = drush_get_option(array('i', 'include'), FALSE)) {

okay

      // System commands, residing in $SHARE_PREFIX/share/drush/commands
      $share_path = drush_get_context('SHARE_PREFIX', '/usr') . '/share/drush/commands';

Interesting, that's exactly what I was looking for! We need to put that in the docs!

      // User commands, residing in ~/.drush
      if (!is_null(drush_server_home())) {
        $searchpath[] = drush_server_home() . '/.drush';

okay

    case DRUSH_BOOTSTRAP_DRUPAL_SITE:
      $searchpath[] = conf_path() . '/modules';
      // Too early for variable_get('install_profile', 'default'); Just use default.
      $searchpath[] = "profiles/default/modules";
      // Add all module paths, even disabled modules. Prefer speed over accuracy.
      $searchpath[] = 'sites/all/modules';
      break;

We end up loading commands of modules which are not enabled (making the fourth item in the docs inaccurate). Actually, do we want to load the commands of disabled modules? What if you have the same function being defined multiple times? Also, many module commands are useless if the module is not enabled (e.g. features, etc.). Maybe we could drop the modules which are not enabled: right now $load_command = TRUE by default, that means if a module specifically does not want to be loaded by drush when disabled, it has to say so via hook_drush_load(), or am I missing some use case here?

    case DRUSH_BOOTSTRAP_DRUPAL_FULL:
      // Add enabled module paths. Since we are bootstrapped,
      // we can use the Drupal API.
      foreach (module_list() as $module) {
        $filename = drupal_get_filename('module', $module);
        $searchpath[] = dirname($filename);
      }

That does not change the fact that we'll also be loading disabled module commands which were added to $searchpath[] just before...

So this would be the suggested documentation for drush_commandfile_list():

/**
 * Collect a list of all available drush command files.
 *
 * Scans the following paths for drush command files:
 *
 * - The "/path/to/drush/commands" folder.
 * - Folders listed in the 'include' option (see example.drushrc.php).
 * - The system-wide commands folder $SHARE_PREFIX/share/drush/commands (e.g. /usr/share/drush/commands)
 * - The ".drush" folder in the user's HOME folder.
 * - All modules in the current Drupal installation.

I'm happy to roll a patch for drush_commandfile_list() including relevant changes for COMMANDS section of README.txt, but first I'd like some feedback on the question re disabled modules commands.

CommentFileSizeAuthor
#5 751986_drush_commandfile_list_docs.patch3.69 KBscor
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

greg.1.anderson’s picture

There's another issue somewhere (closed by now?) that discusses the disabled module issue in depth. Basically it boils down to performance. drush_commandfile_list() was modified to exclude duplicate command files, should you have the same drush commandfile show up in multiple places that drush searches.

$load_command / .drush.load.inc was added to allow command files to decide if they wanted to be loaded or not based on external conditions (e.g. command line args, etc.). This is mostly useful for disabling command files that contain only hooks. It defaults to 'true' since most command files will not want to disable themselves (no .drush.load.inc file == enabled). This feature has nothing to do with Drupal module enabled/disabled status, though.

moshe weitzman’s picture

Right. Go find that issue that Greg referenced.

The help command loads all drush.inc files regardless of whether the module is enabled or not. Its too slow to bootstrap drupal to find out for sure. We need commands to start listing in help as quickly as possible.

Right now, a command that depends on an accompanying module should declare that dependency in its command definition. See devel_generate.drush.inc as an example.

Patches to doxygen and README are much appreciated.

scor’s picture

This feature has nothing to do with Drupal module enabled/disabled status, though.

Right, as I understand it, all module commands will be loaded by default no matter what status they have on the site, unless they specify via .drush.load.inc that they want to opt out.

case DRUSH_BOOTSTRAP_DRUPAL_FULL:
      // Add enabled module paths. Since we are bootstrapped,
      // we can use the Drupal API.
      foreach (module_list() as $module) {
        $filename = drupal_get_filename('module', $module);
        $searchpath[] = dirname($filename);
      }

This step is, as a result, quite redundant as we're running a recursive drush_scan_directory() on each modules folder which have already been scanned before in DRUSH_BOOTSTRAP_DRUPAL_SITE (that includes the all core enabled module which should not contain any drush commands). I guess that's fine as long as disk lookups and drush_scan_directory() are cheap. The only case where we'll find new commands is if modules are stored outside the recommended locations (i.e. sites/all/modules and sites/mysite/modules). Anyways, just thinking out loud here, so I'm going to roll a patch now.

moshe weitzman’s picture

It does seem like we could remove the DRUSH_BOOTSTRAP_DRUPAL_FULL code and only bootstrap to SITE level. That would be way faster. Seems too good to be true.

scor’s picture

Status: Active » Needs review
FileSize
3.69 KB
greg.1.anderson’s picture

Status: Needs review » Reviewed & tested by the community

This looks good to me.

Regarding bootstrapping, of course function _drush_bootstrap_drupal_full() is still necessary, but I think that you are right that we potentially could remove DRUSH_BOOTSTRAP_DRUPAL_FULL from drush_commandfile_list(). The reason that we might not want to is that module_list() is part of the Drupal API, and its functionality might vary from version to version, so there's no saying that there might someday be a hook or something that caused modules to be loaded from new and strange locations. Also, the comment in the SITE level bootstrap says // Too early for variable_get('install_profile', 'default'); Just use default., so there might be some non-default-profile-supplied modules added at the DRUSH_BOOTSTRAP_DRUPAL_FULL stage. I'm uncertain whether the utility of doing so is high enough to warrant the performance hit, but I'm inclined to leave well enough alone, I think....

moshe weitzman’s picture

Status: Reviewed & tested by the community » Fixed

Agreed. We'll leave the FULL code there .. Committed latest patch. Thanks.

scor’s picture

For the records, here are the times spent in _drush_find_commandfiles() expressed in us.

Core only:
pass 1:  14266.0140991
pass 2:     47.9221343994
pass 3:   4152.05955505
total: 18.3ms

Core only without DRUSH_BOOTSTRAP_DRUPAL_FULL case:
pass 1: 14595.9854126
pass 2:    48.8758087158
pass 3:    12.8746032715
total: 14.6ms

Drupal 6 site with 179 modules in total (enabled + disabled): 
pass 1:  14439.8212433
pass 2: 114576.816559
pass 3: 174199.819565
total: 303.2ms

without DRUSH_BOOTSTRAP_DRUPAL_FULL case:
pass 1:14472.0077515
pass 2:114170.074463
pass 3:13.8282775879
total: 128ms

As expected, it's the last pass in _drush_find_commandfiles() that takes the most time when there are many modules on the site. The time difference is 175ms for almost 200 modules. I don't think it's that bad and I'm sure we can live with it.

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.