On Windows, PHP's DIRECTORY_SEPARATOR constant returns "\". But if you're running a Unix shell, you'll need a forward slash. The attached patch replaces DIRECTORY_SEPARATOR with DRUSH_DIRECTORY_SEPARATOR and allows the user to specify the separator.

PHP's filesystem functions on Windows work with either \ or /. (is_dir('c:/dir1/dir2') and is_dir('c:\dir1\dir2') will both return TRUE). But anytime there was an rtrim($path, DIRECTORY_SEPARATOR) things went downhill.

A simple example: Before:

mikeker@MIKEKER2 /d/Documents/dev/test
$ drush dl
Destination directory c:\windows\temp/drush_tmp_1300899773/drupal-7.0 already exists.                            [error]

Project drupal (7.0) downloaded to d:/Documents/dev/test/drupal-7.0.                                           [success]

After adding $options['directory_separator'] = '/'; to drushrc.php:

mikeker@MIKEKER2 /d/Documents/dev/test
$ drush dl
Project drupal (7.0) downloaded to d:/Documents/dev/test/drupal-7.0.                                           [success]

(This is the first patch I've built using Git, so please let me know if anything is hosed... Thanks.)

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

greg.1.anderson’s picture

Status: Needs review » Needs work

When you say "if you are running a Windows shell", does that mean that you are running under cygwin? If so, it sounds like perhaps DIRECTORY_SEPARATOR is not correct in your environment.

DIRECTORY_SEPARATOR is not supposed to be used to compose paths; it is supposed to be used with rtrim and other operations on paths handed to you by the system.

I don't think that it is necessary or correct to let the user set the directory separator. In theory, PHP should set it correctly. If PHP does not set it correctly, then ideally drush would be able to determine what it should be, and set it to the correct value automatically. I do not know if this is possible, but it seems that it should be.

mikeker’s picture

When you say "if you are running a Windows shell", does that mean that you are running under cygwin?

Yes. I'm running the Git shell on Windows. I believe the Git (I'm on v1.7.4.1) shell runs on Msys, which is very similar to Cygwin. Anyhow, if I put

print 'directory separator is ' . DIRECTORY_SEPARATOR . "\n";
exit;

I get "\" from both the browser and the command prompt. (FYI: I'm running WAMP server 2.0 with PHP 5.2.11 on Windows 7.)

Is there a way to set this via some PHP config? My understanding was this constant was baked into PHP based on the native environment. And PHP is running as a native Windows app and not as part of a Cygwin/Msys shell. But I'm well above my pay grade talking about what's built into PHP...

DIRECTORY_SEPARATOR is not supposed to be used to compose paths; it is supposed to be used with rtrim and other operations on paths handed to you by the system.

Agreed. Since PHP on Windows accepts either / or \ as a directory separator, I'll remove the bit from drush_find_tmp().

However the rest of the patch deals with processing return values from drush_shell_exec_output() which, when executed in a Cygwin/Msys shell, return paths with / as the directory separator even on Windows

Perhaps the option should be more general? Something like "running_shell_on_windows"?

greg.1.anderson’s picture

I think that your names are just fine; DRUSH_DIRECTORY_SEPARATOR is a good idea if (as seems to be the case) DIRECTORY_SEPARATOR is wrong for some reason. I object to the option because as you said, it takes rather exacting knowledge about PHP and the OS to set this value correctly, so I think it would cause problems to put the task of selection into the hands of the user.

How about this logic: if DIRECTORY_SEPARATOR is "\" and the path to drush (or any other readily-available path) does not contain a "\" but does contain a "/", then set DRUSH_DIRECTORY_SEPARATOR to "/", otherwise set it to DIRECTORY_SEPARATOR.

mikeker’s picture

I agree that there is some detailed knowledge about PHP and your OS and what shell you're using to set the value correctly. The patch currently sets DRUSH_DIRECTORY_SEPARATOR = DIRECTORY_SEPARATOR if there is no option specified. This is how things currently work without the patch. But the patch lets those that do know enough about their PHP/OS/shell config to specify a different separator if they know what they're doing.

I'm concerned that trying to do the automagical settings will result in edge cases causing problems. The current patch leaves things as-is unless a knowledgeable user specifies something different.

greg.1.anderson’s picture

My opinion is unchanged (c.f. #1 and #3), but perhaps other maintainers will disagree.

mikeker’s picture

How about settings DRUSH_DIRECTORY_SEPARATOR as such:

  if (drush_is_windows()) {
    drush_shell_exec('pwd');
    $output = drush_shell_exec_output();
    if (FALSE === strstr($output, '/')) {
      define('DRUSH_DIRECTORY_SEPARATOR', '\\');
    }
    else {
      define('DRUSH_DIRECTORY_SEPARATOR', '/');
    }
  }
  else {
    define('DRUSH_DIRECTORY_SEPARATOR', DIRECTORY_SEPARATOR);
  }

I couldn't use DRUSH_COMMAND as that has both forward and backslashes in it ($_SERVER['argv'][0] has them).

Or is there another readily available path we could use instead of another shell_exec? I'm not all that familiar with the Drush bootstrap process.

If this is agreeable, I'll roll another patch. Thanks.

mikeker’s picture

My mistake, most $_SERVER entries are forwardslashed, but realpath() converts those to backslashes on Windows. The above can shrink to

  if (drush_is_windows()) {
    if (FALSE === strstr($_SERVER['argv'][0], '/')) {
      define('DRUSH_DIRECTORY_SEPARATOR', '\\');
    }
    else {
      define('DRUSH_DIRECTORY_SEPARATOR', '/');
    }
greg.1.anderson’s picture

Status: Needs work » Needs review

I like #7.

mikeker’s picture

Title: DIRECTORY_SEPARATOR causes errors on Windows running CygWin/mSys/Git shell » DIRECTORY_SEPARATOR causes errors on Windows running CygWin/Msys/Git shell
FileSize
18.61 KB

OK, re-rolled using the logic in #7 instead. This has the added bonus of letting us set DRUSH_DIRECTORY_SEPARATOR earlier in the bootstrap process so we get the correct path in drush_find_drush.

I also worked on the fact that realpath() on Windows forces the backslash separator by refactoring realpath to drush_realpath and dealing with the changes there.

Feedback is appreciated! Thanks.

greg.1.anderson’s picture

Status: Needs review » Needs work

I'm not sure that I'm cool with #9. There is something I need to understand first. You say that realpath converts forward slashes to backslashes on Windows, and that sometimes we do not want that.

Is the path with backslashes usable, or is it unusable? If it is not usable, then I think that maybe your system is not configured correctly, and we should re-evaluate the premise of this whole patch.

If the only problem is that converting from a slash to a backslash confuses our trim operations, then I think it might be cleaner to have a drush_trim_path function that is special-cased for Windows, and dispense with separator detection and drush_realpath. Would it work to just trim / and \ on Windows? Maybe we only do this if the OS is Windows and the filesystem returns paths with / in them. I have to admit that it is a little mysterious to me why the filesystem would ever return paths with / in them if realpath was also correcting path separators back to \. Seems very odd.

mikeker’s picture

Some background which may or may not be needed: realpath conversion of slashes on Windows: http://us3.php.net/manual/en/function.realpath.php -- see example #2. Forward slash is the universally accepted directory separator: http://php.net/manual/en/function.dirname.php, see path parameter description.

True, running a Cygwin (or similar) shell on Windows makes backslashes unusable but I do not see this as a system configuration issue. You are correct that this is not the standard Windows/DOS behavior. But anyone that has installed a *nix shell on Windows will be in the same boat -- which I suspect will be a growing portion of the Windows Drupal community as they start working with Git. Also, this patch protects those that are running Windows without a shell by checking the return of $_SERVER['argv'][0]. In a non-shell config, it returns c:\bin\drush\ while in a shell config it returns /c/bin/drush or /cygwin/c/bin/drush. DRUSH_DIRECTORY_SEPARATOR is set accordingly.

I have to admit that it is a little mysterious to me why the filesystem would ever return paths with / in them if realpath was also correcting path separators back to \. Seems very odd.

I think the confusion is because PHP on Windows has some baked-in assumptions about directory separators (backslash) while a Cywin or similar shell tries to mimic the *nix environment with forward slash directory separators. So PHP assumes backslashes while Cygwin (via exec) returns forward slashes.

Oh, if we could all just speak the same language! ;)

Thanks.

greg.1.anderson’s picture

Yes, I understand that the patch protects people not running in a shell; however, it is my opinion that introducing drush_realpath is overkill unless it is absolutely necessary to do so.

True, running a Cygwin (or similar) shell on Windows makes backslashes unusable

Is that the case? You also have a comment in your patch that says that either kind of path can be used with PHP on Windows. That is why I suggested that maybe all we really need is a wrapper for a path-trim function.

Drupal code writes paths into the database (and Drupal does write paths into the database). Drush will cause Drupal code to run in various circumstances, so if you call the same path-access code sometimes from Drush and sometimes from the web server, then you might end up with your database containing some paths with / and some paths with \. If either kind of path works in either mode, then this is not a problem, but if this is a problem, then it will take a lot more work to fix (e.g. drush_realpath may be necessary).

It is very important to determine if this problem (avoiding paths with \ under a Windows shell) needs to be fixed. Maybe these paths only need to be fixed up if you pass a path to cygwin via exec, but they work in php mode? If this is the case, then we might need to introduce drush_path_alter and insure that all paths are sent through it prior to calling exec.

mikeker’s picture

My brain is starting to get a little knotted up by this discussion, so I'm going to state some basics just to make sure I'm on the same track as Greg... Perhaps this is how I should've started this issue, but it seemed like such a small change at first! Please feel free to correct anything I'm missing.

I see three scenarios that involve directory separators:

  1. Building paths
  2. Dealing with paths returned from PHP (eg: getcwd() or realpath())
  3. Dealing with paths returned from exec()

For #1: we should use the forward slash everytime as it works for Windows (accepts both) and *nix (accepts only forward slashes). That seems to be how Drupal core does it in looking at includes/file.inc. eg: file_create_filename()

For #2: PHP for Windows returns backslashes while PHP for *nix returns forwardslashes. This seems to be baked into PHP and not configurable via $_ENV or similar. See this PHP doc page.

For #3: exec() returns using the directory separator of the underlying command. Things like tar return forwardslashes --at least for Cygwin and Msys, the only two I've unsed. I have no idea what happens if Drush exec's a DOS command in a Cygwin environment... I assume it would return with backslashes? But I couldn't find any examples where Drush exec'ed anything other than *nix commands so perhaps it's not an problem.

Additional headaches arise when using Windows to remotely control a *nix box or the reverse.

My suggestion is to have Drush internally convert any dir separators to forwardslashes -- that way they execute on any OS, eliminating the Windows remotely controling a Linux box issue. There already exists _drush_convert_path() which does this. (Note: I need to update my earlier patch to use this rather than str_replace). The above patch also changes the one remaining place where Drush had hardcoded backslashes (drush_find_tmp).

That leaves paths returned by PHP and ensuring that Drush only calls exec on *nix-style commands. My hope was that add drush_realpath that we could deal with the baked-into-PHP backslashes there. While drush_path_trim (or similar) would be a useful addition, it won't deal with the backslashes within returned by a call to realpath().

I don't think we need anything as nuclear as drush_path_alter if we always use forwardslashes for outbound paths. (Meaning paths passed to the system rather than return values being parsed by Drush). Internally, Drupal seems to build paths with forwardslashes and I've not seem any backslashes in the DB, even on sites that have run exclusively on my Windows dev machine.

I'll reroll the patch to use _drush_convert_path() and add a drush_path_trim function to replace the use of rtrim. But I think that drush_realpath is required.

Thanks.

greg.1.anderson’s picture

#13 sounds basically correct, but must be amended slightly:

1a. Building paths for use by PHP
1b. Building paths to be passed as arguments to drush_shell_exec or drush_op_system.

1a is covered quite well in #13 above. For 1b, we might need drush_path_alter (the reverse operation of drush_convert_path) if exec on Windows expects arguments that are paths to files and folders to contain \ rather than / characters. It seems to me that this would be the case; for example, in pm_download, we have drush_shell_exec("wget -P . %s", $release['download_link']). It is my assumption that $release['download_link'] contains / rather than \ separators (per #13), but we also know that pm-download works on Windows. This does imply that drush_path_alter is not necessary, but I'm not sure why it currently works. Is this because, as you imply above, Unix commands expect / paths, not \ paths? This implies that pm-download does not work when called from DOS via drush.bat -- unless in this mode all paths contain \, and / paths never exist.

Internally, Drupal seems to build paths with forwardslashes and I've not seem any backslashes in the DB, even on sites that have run exclusively on my Windows dev machine.

Interesting, considering that in this environment, DIRECTORY_SEPARATOR must be \, isn't it?

mikeker’s picture

Hmmm... I had forgotten about the drush.bat environment. I'll have to try that out.

Drupal doesn't use DIRECTORY_SEPARATOR in core on D6. In D7, it shows up in a few places: includes/database/log.inc and modules/simpletest/filetransfer.test. In log.inc it's parsing the return from debug_backtrace(), which would have backslashes on Windows, and in simpletest it's building paths to pass to PHP which can use either on Windows.

I can't say this with certainty, but it seems that Drupal core is building paths with the forwardslash only (except for the simpletest example). Which is good...

1a. Building paths for use by PHP
1b. Building paths to be passed as arguments to drush_shell_exec or drush_op_system.

re: 1b: Again, I can't speak with certainty here, but on the two shells I've worked with (Cygwin and Msys) shell commands accept forwardslashes and return filepaths with forwardslashes (eg: tar -tf returns "drupal-7.0/..."). And since *nix boxes only take forwardslashes, if we make the forwardslash universal, it will (hopefully) eliminate some of the Windows -> Linux remote issues. I don't believe there's a need to convert exec'ed or system'ed paths to backslash on Windows. (Note: need to verify this w/ drush.bat).

I'll just throw this out as crazy-talk: What about eliminating drush.bat and requiring anyone using Drush on Windows to have a shell? They've already got PHP. They ought to be installing Git anyhow, right? Git comes with a shell that works great with Drush (once you add wget). Having a more limited config on Windows would help us improve Drush support on Windows. Through this process I've been working on instructions on setting up a Drupal dev environment on Windows, a stripped down version of which could be used to setup Drush on Windows.

greg.1.anderson’s picture

We will need to at least support Powershell, which I believe is bundled here:

http://www.microsoft.com/web/downloads/platform.aspx

It looks like Powershell uses backslashes in its path arguments, just like DOS.

I don't have a Windows 7 install right now, but in a few weeks I'll set it up and help answer some of these open questions. It would be helpful if kulov could also comment here.

mikeker’s picture

Just wanted to leave a quick comment that I'm still interested and working on this issue. It's just those pesky paying customers keep getting in the way... More coming (hopefully) in a week or so.

greg.1.anderson’s picture

Thanks for the update. It looks like we are not (necessarily) going to support Powershell, but will instead use winrm to execute remote commands. Winrm runs commands via DOS, so we need to support backslashes too. Powershell may or may not be the local shell. We are not currently planning on supporting cygwin, but your contributions toward this end might make this possible.

greg.1.anderson’s picture

Assigned: Unassigned » greg.1.anderson
Issue tags: +Windows

See also #1062962: drush dd on windows returns invalid path. kulov and I are starting to work on drush + windows this week, but probably won't be touching cygwin support.

greg.1.anderson’s picture

Status: Needs work » Closed (duplicate)

DIRECTORY_SEPARATOR issue addressed in #766080: Windows support for drush: escaping the path to drush in backend invoke and elsewhere, perhaps not fully satisfactorily yet, but you will find some useful code there. cygwin / msysgit not supported yet. Help out in that issue if interested in continuing this work.