Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[READY] Enable windows style flags when --driver-mode=cl is found in flags #789

Merged
merged 1 commit into from
Feb 7, 2018

Conversation

bstaletic
Copy link
Collaborator

@bstaletic bstaletic commented Jul 4, 2017

Windows (cl) style flags are currently filtered and thus windows users are unable to use a .ycm_extra_conf.py such as this one:

def FlagsForFile( filename ):
    return { 'flags': [
                '--driver-mode=cl',
                '/c',
                '/Zi',
                '/nologo',
                '/W3',
                '/WX-',
                '/O2',
                '/GL',
                '/Gm-',
                '/EHsc',
                '/MT',
                '/GS',
                '/fp:precise',
                '/Zc:wchar_t',
                '/Zc:forScope',
                '/Zc:inline',
                '/Gd',
                '/TP',
                '/wd4099',
                '/Tp' ] }

If --driver-mode=cl is in flags, libclang gladly accepts these flags. This pull request checks for --driver-mode=cl before filtering the flags. U used the above .ycm_extra_conf.py to confirm if everything works. The resulting :YcmDebugInfo:

Printing YouCompleteMe debug information...
-- Client logfile: /tmp/ycm_rbjk7yrj.log
-- Server Python interpreter: /usr/sbin/python
-- Server Python version: 3.6.1
-- Server has Clang support compiled in: True
-- Clang version: clang version 4.0.1 (tags/RELEASE_401/final)
-- Extra configuration file found and loaded
-- Extra configuration path: /home/bstaletic/Temp/ycmd/test/.ycm_extra_conf.py
-- C-family completer debug information:
--   Compilation database path: None
--   Flags: ['--driver-mode=cl', '-resource-dir=/home/bstaletic/.vim/pack/minpac/start/YouCompleteMe/third_party/ycmd/ycmd/../clang_includes', '/c', '/Zi', '/nologo', '/W3',
'/WX-', '/O2', '/GL', '/Gm-', '/EHsc', '/MT', '/GS', '/fp:precise', '/Zc:wchar_t', '/Zc:forScope', '/Zc:inline', '/Gd', '/TP', '/wd4099', '/Tp', '-fspell-checking']
-- Server running at: http://127.0.0.1:51101
-- Server process ID: 16027
-- Server logfiles:
--   /tmp/ycmd_51101_stdout_rh2xfnry.log
--   /tmp/ycmd_51101_stderr_7_n_feiy.log

Fixes ycm-core/YouCompleteMe#2491


This change is Reviewable

@codecov-io
Copy link

codecov-io commented Jul 4, 2017

Codecov Report

Merging #789 into master will increase coverage by <.01%.
The diff coverage is 100%.

@@            Coverage Diff             @@
##           master     #789      +/-   ##
==========================================
+ Coverage   96.27%   96.28%   +<.01%     
==========================================
  Files          84       84              
  Lines        6504     6519      +15     
==========================================
+ Hits         6262     6277      +15     
  Misses        242      242

@micbou
Copy link
Collaborator

micbou commented Jul 4, 2017

Thanks for the PR.


Reviewed 2 of 2 files at r1.
Review status: all files reviewed at latest revision, 3 unresolved discussions.


ycmd/completers/cpp/flags.py, line 406 at r1 (raw file):

    if flag == '--driver-mode=cl':
      enable_windows_style_flags = True

This variable should be set before looping through the flags, otherwise, Windows flags defined before --driver-mode=cl will be ignored. We could simply set enable_windows_style_flags like this:

enable_windows_style_flags = '--driver-mode=cl' in flags

or if we want to be more correct, we should find the last occurrence of --driver-mode= in the list of flags and check if its value is cl or not. Probably not worth the trouble.


ycmd/completers/cpp/flags.py, line 433 at r1 (raw file):

  if enable_windows_style_flags:
    new_flags += _FilterWindowsFlags( flags_without_preceeding_dash )

This doesn't keep the order of flags which I think is important. If enable_windows_style_flags is set before, this can be done in the loop.


Comments from Reviewable

@bstaletic bstaletic force-pushed the windows_style_flags branch from 3e61b03 to 1581d48 Compare July 5, 2017 02:32
@bstaletic
Copy link
Collaborator Author

Review status: 1 of 2 files reviewed at latest revision, 2 unresolved discussions.


ycmd/completers/cpp/flags.py, line 406 at r1 (raw file):

Previously, micbou wrote…

This variable should be set before looping through the flags, otherwise, Windows flags defined before --driver-mode=cl will be ignored. We could simply set enable_windows_style_flags like this:

enable_windows_style_flags = '--driver-mode=cl' in flags

or if we want to be more correct, we should find the last occurrence of --driver-mode= in the list of flags and check if its value is cl or not. Probably not worth the trouble.

Done. I included the "more correct" checking as well.


ycmd/completers/cpp/flags.py, line 433 at r1 (raw file):

Previously, micbou wrote…

This doesn't keep the order of flags which I think is important. If enable_windows_style_flags is set before, this can be done in the loop.

Done. It occured to me that it was not preserving order though.


Comments from Reviewable

@micbou
Copy link
Collaborator

micbou commented Jul 5, 2017

Reviewed 1 of 1 files at r2.
Review status: all files reviewed at latest revision, 1 unresolved discussion.


ycmd/completers/cpp/flags.py, line 428 at r2 (raw file):

           ( not previous_flag_is_include and '/' in flag ) ) and
             ( not enable_windows_style_flags or
               ( enable_windows_style_flags and os.path.exists( flag ) ) ) ):

This can be simplified to:

not enable_windows_style_flags or os.path.exists( flags )

Comments from Reviewable

@bstaletic bstaletic force-pushed the windows_style_flags branch from 1581d48 to 9e181f7 Compare July 5, 2017 06:33
@bstaletic
Copy link
Collaborator Author

Review status: 1 of 2 files reviewed at latest revision, 1 unresolved discussion.


ycmd/completers/cpp/flags.py, line 428 at r2 (raw file):

Previously, micbou wrote…

This can be simplified to:

not enable_windows_style_flags or os.path.exists( flags )

Done.


Comments from Reviewable

@Valloric
Copy link
Member

Valloric commented Jul 6, 2017

Review status: 1 of 2 files reviewed at latest revision, 1 unresolved discussion.


ycmd/completers/cpp/flags.py, line 397 at r3 (raw file):

  for flag in flags:
    if flag.startswith( '--driver-mode' ):

Why don't we just use if flag == ''--driver-mode=cl''? I see there was some discussion about making sure we look at the last entry, but the current code is functionally identical to just a simple string equality check.

So I don't see the benefit of the startswith part.

We could look at the last --driver-mode flag, but then this could would still need to be changed.


Comments from Reviewable

@bstaletic
Copy link
Collaborator Author

Review status: 1 of 2 files reviewed at latest revision, 1 unresolved discussion.


ycmd/completers/cpp/flags.py, line 397 at r3 (raw file):

Previously, Valloric (Val Markovic) wrote…

Why don't we just use if flag == ''--driver-mode=cl''? I see there was some discussion about making sure we look at the last entry, but the current code is functionally identical to just a simple string equality check.

So I don't see the benefit of the startswith part.

We could look at the last --driver-mode flag, but then this could would still need to be changed.

If we used if flag == '--driver-mode=cl' while looping throught all flags, enable_windows_style_flags would end up having the value of the latest comparison.

If we do the flag == '--driver-mode=cl' comparison only when flag.startswith( '--driver-mode' ) then enable_windows_style_flags will have the bool value set the last time a flag started with --driver-mode.


Comments from Reviewable

@Valloric
Copy link
Member

Valloric commented Jul 7, 2017

Review status: 1 of 2 files reviewed at latest revision, 1 unresolved discussion.


ycmd/completers/cpp/flags.py, line 397 at r3 (raw file):

Previously, bstaletic wrote…

If we used if flag == '--driver-mode=cl' while looping throught all flags, enable_windows_style_flags would end up having the value of the latest comparison.

If we do the flag == '--driver-mode=cl' comparison only when flag.startswith( '--driver-mode' ) then enable_windows_style_flags will have the bool value set the last time a flag started with --driver-mode.

Ah, you're right, my bad! Make sure to add a test for this; I can totally see someone making the same mistake in the future.


Comments from Reviewable

@Valloric
Copy link
Member

Valloric commented Jul 7, 2017

:lgtm: with a test for the --driver-mode flag position stuff.


Review status: 1 of 2 files reviewed at latest revision, 1 unresolved discussion.


Comments from Reviewable

@bstaletic bstaletic force-pushed the windows_style_flags branch from 9e181f7 to 359ac96 Compare July 8, 2017 16:36
@bstaletic bstaletic changed the title Enable windows style flags when --driver-mode=cl is found in flags [READY] Enable windows style flags when --driver-mode=cl is found in flags Jul 8, 2017
@bstaletic bstaletic force-pushed the windows_style_flags branch from 359ac96 to 02a2942 Compare August 19, 2017 18:08
@bstaletic
Copy link
Collaborator Author

bstaletic commented Aug 30, 2017

I found a bug in the new code. I'll be debugging this tonight.
If anyone is interested, it picks up windows style flags from an index one before the first --driver-mode flag,

Once again, sorry for dragging on forever what should have been a quick PR.


Review status: 1 of 2 files reviewed at latest revision, 1 unresolved discussion.


Comments from Reviewable

@bstaletic bstaletic changed the title [READY] Enable windows style flags when --driver-mode=cl is found in flags [WIP] Enable windows style flags when --driver-mode=cl is found in flags Oct 6, 2017
@crapp
Copy link

crapp commented Nov 2, 2017

Whats the status of this PR? Any help needed?

@bstaletic
Copy link
Collaborator Author

Here's the problem.

_RemoveFlagsPrecedingCompiler() currently removes a lot. It assumes that the first flag before the one starting with a dash is the name of the compiler and removes the rest.

If we change the function to:

def _RemoveFlagsPrecedingCompiler( flags ):
  """Assuming that the flag just before the first flag (which starts with a
  dash) is the compiler path, removes all flags preceding it."""

  for index, flag in enumerate( flags ):
    if flag.startswith( '-' ) or ( flag.startswith( '/' ) and not os.path.isfile( flag ) ):
      return ( flags[ index - 1: ] if index > 1 else
               flags )
  return flags[ :-1 ]

Then the fuction would not strip too much. So far so good.
Then the real problem begins. This change breaks a ton of tests.
This is because with --driver-mode=cl there is no way to distinguish between an absolute path and a flag.

So I gave it a fight two days in a row. And every time I fixed something five other things would break.
At this point it's really annoying and frankly this PR doesn't scratch my own itch. But I don't think it's okay to just close a PR on the account of "being lazy". So now I'm hoping someone would jump in and do the rest of the work.

@crapp
Copy link

crapp commented Nov 2, 2017

OK I'll have a look at it because for me it would be very helpful to use YouCompleteme with clang-cl driver mode.

@bstaletic bstaletic force-pushed the windows_style_flags branch 3 times, most recently from feb5318 to fcbbacd Compare December 25, 2017 12:27
@bstaletic
Copy link
Collaborator Author

Finally made this work, as far as I can tell.
I wouldn't be surprised if I overlooked some corner case.


Review status: 0 of 2 files reviewed at latest revision, 2 unresolved discussions.


ycmd/completers/cpp/flags.py, line 397 at r3 (raw file):

Previously, Valloric (Val Markovic) wrote…

Ah, you're right, my bad! Make sure to add a test for this; I can totally see someone making the same mistake in the future.

I believe the provided tests are enough to prevent this mistake from happening.


ycmd/completers/cpp/flags.py, line 577 at r4 (raw file):

    else:
      # TODO: Should we care about `/I` when enable_windows_style_flags
      for path_flag in PATH_FLAGS:

My current solution simply does the following:

INCLUDE_FLAGS_WIN_STYLE = [ '/I' ]
PATH_FLAGS =  [ '--sysroot=' ] + INCLUDE_FLAGS + INCLUDE_FLAGS_WIN_STYLE

Comments from Reviewable

@crapp
Copy link

crapp commented Dec 26, 2017

@bstaletic Thank you so much. I won't have time to play with this until the 8th but I'll report back.

@bstaletic
Copy link
Collaborator Author

@crapp I'll look forward to your review. Might be the first real world test of this.


Review status: 0 of 2 files reviewed at latest revision, 2 unresolved discussions.


Comments from Reviewable

@puremourning
Copy link
Member

I realise this is still RFC, but I feel like there are now a bunch of special cases where we probably need more tests.


Review status: 0 of 2 files reviewed at latest revision, 6 unresolved discussions.


ycmd/completers/cpp/flags.py, line 577 at r4 (raw file):

Previously, bstaletic (Boris Staletic) wrote…

My current solution simply does the following:

INCLUDE_FLAGS_WIN_STYLE = [ '/I' ]
PATH_FLAGS =  [ '--sysroot=' ] + INCLUDE_FLAGS + INCLUDE_FLAGS_WIN_STYLE

That seems reasonable, though a more complete solution would be:

  path_flags = [ PATH_FLAGS ]
  if enable_windows_style_flags:
      path_flags.extend( WINDOWS_PATH_FLAGS )
  for path_flag in path_flags:

I guess in theory there is a chance that when driver mode is not cl, a real path could be /I


ycmd/completers/cpp/flags.py, line 118 at r5 (raw file):

    for flag in flags:
      if flag.startswith( '--driver-mode' ):
        self.enable_windows_style_flags = ( flag == '--driver-mode=cl' )

It feels strange that _ShouldXYZ has the side effect of calculating self.XYZ. Perhaps the method should be called _CheckForWindowsStyleFlags ?

Then again, it doesn't look like self._enable_windows_style_flags needs to exist in self ? could this method not just return True/False ?


ycmd/completers/cpp/flags.py, line 161 at r5 (raw file):

                                            filename,
                                            add_extra_clang_flags,
                                            self.enable_windows_style_flags )

why do we store this as a member ?


ycmd/completers/cpp/flags.py, line 326 at r5 (raw file):

def _RemoveFlagsPrecedingCompiler( flags, enable_windows_style_flags ):
  """Assuming that the flag just before the first flag (which starts with a

probably needs updating


ycmd/completers/cpp/flags.py, line 354 at r5 (raw file):

  first_flag = flags[ 0 ]

  if ( not first_flag.startswith( '-' ) and

does this need to to check for windows flags too ? e.g. and not (windows and starts with /)


Comments from Reviewable

@bstaletic
Copy link
Collaborator Author

there are now a bunch of special cases

I agree that the code feels clumsy and isn't very pretty to look at. And I'm being nice to myself and this code I've written.
I'll gladly add more tests, but would like it if I got some hint, as in "test this case".


Review status: 0 of 2 files reviewed at latest revision, 6 unresolved discussions.


ycmd/completers/cpp/flags.py, line 577 at r4 (raw file):

I guess in theory there is a chance that when driver mode is not cl, a real path could be /I

That's correct, but I would argue /I, as an absolute path in one's project, isn't sane. Yet I have seen I deep in te file hierarchy as an include path in some projects.

I was thinking of propagating the above self.enable_windows_style_flags as a parameter all the way to _MakeRelativeePathsInFlagsAbsolute(), but at the time didn't find an obvious way to do it. I'll take another look soon.


ycmd/completers/cpp/flags.py, line 118 at r5 (raw file):

Previously, puremourning (Ben Jackson) wrote…

It feels strange that _ShouldXYZ has the side effect of calculating self.XYZ. Perhaps the method should be called _CheckForWindowsStyleFlags ?

Then again, it doesn't look like self._enable_windows_style_flags needs to exist in self ? could this method not just return True/False ?

Now that I think of it, yes this could just return True or False.
So should I make it return True/False and just assign the value?


ycmd/completers/cpp/flags.py, line 161 at r5 (raw file):

Previously, puremourning (Ben Jackson) wrote…

why do we store this as a member ?

Because that's the first thing that came to my mind after thinking "I'll need this boolean in more than one method". If you have a better idea, I'm listening.


ycmd/completers/cpp/flags.py, line 326 at r5 (raw file):

Previously, puremourning (Ben Jackson) wrote…

probably needs updating

My idea was:

  1. Flags starting with a dash are always accepted.
  2. Windows style flags are accepted when --driver-mode=cl is present and the flag is not a file.
  3. So the thing before the first flag (either 1. or 2.) is the compiler.

It did occur to me that this might be wrong now.
That's why I asked on gitter why this assumption is a good on. But I received no answer.


ycmd/completers/cpp/flags.py, line 354 at r5 (raw file):

Previously, puremourning (Ben Jackson) wrote…

does this need to to check for windows flags too ? e.g. and not (windows and starts with /)

I think not. Definitely not how you have written it. Here's why.

  • Because of _RemoveFlagsPrecedingCompiler, irrelevant of enable_windows_style_flags, the frst flag is (at least should be) either the compiler, a Win style flag, or starts with a dash.
  • If it doesn't start wth a dash, it's either an absolute path, a win style flag or a C++ compiler from $PATH.
    • If it starts with a forward slash it's either a win flag or an absolute path.
      • If it matches the reges (I think) it's safe to assume that's a compiler path.
      • If it doesn't match it could still be a win style flag or some stray absolute path - which is determined later and cleaned properly.
    • If it starts with anything else, it's a stray file path and should be gotten rid of in _RemoveUnusedFlags().

If we do it like you suggested you we would break the case where /usr/bin/g++ is the first flag, but later we also have --driver-mode=cl.
Yes, this is messy...


Comments from Reviewable

@bstaletic bstaletic changed the title [WIP] Enable windows style flags when --driver-mode=cl is found in flags [RFC] Enable windows style flags when --driver-mode=cl is found in flags Dec 27, 2017
@bstaletic
Copy link
Collaborator Author

Sorry, taking awhile, I've had distractions. I'm working on checking out the patch.

No worries, take your time.

The compile_commands.json file is generated by CMake automatically.

Yes, and you should be able to do set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --driver-mode=cl"). @puremourning's suggestion should also work.

@joelfrederico
Copy link

I hacked your patch to just simply return true just to be safe. It... sort of works... The flags look correct now in YcmDebugInfo, but I'm getting "ycm_core.ClangParseError: An AST deserialization error occurred while parsing the translation unit."

@puremourning
Copy link
Member

Are you using MSVC 2017 ? Current libclang has problems with the latest MSVC standard library headers, leading to this problem.

The only solution at the moment is to build a trunk clang. It might be resolved with clang 6 in the next month or so.

@joelfrederico
Copy link

@bstaletic It seems like your patch works, for sure. The flags are correct.

@puremourning Ah that might be why. I'm upgrading to MSVC 2017 as we speak.

@joelfrederico
Copy link

joelfrederico commented Feb 21, 2018

@puremourning Wait, apologies- did you mean I should use MSVC 2017 to compile ycmd? Or do you mean that my project should use the STL that comes with MSVC 2017?

@puremourning
Copy link
Member

I mean that if your project uses the standard library in the latest MSVC, you'll get these errors from ycmd (or any other libclang-based parser using the latest released libclang). This is because libclang 5.0.1 (the latest release version) chokes on the MSVS standard library headers for some reason (I don't know why).

It's fixed in upstream clang trunk, and might be fixed in clang 6 (and thus libclang 6). One solution might be to build clang from source (either clang 6 release candidate, or trunk) and use the YCM "full installation guide" to rebuild ycmd pointing at that build of clang.

@joelfrederico
Copy link

@puremourning Ah. In that case, I'm still using MSVC 14 2015, not 2017. Does that version of the STL have this issue?

@micbou
Copy link
Collaborator

micbou commented Feb 21, 2018

Do you get the error each time you are trying to complete or it only happens once?

@puremourning
Copy link
Member

Well, it would appear that libclang is choking on your code or the STL, though I haven't heard it reported against MSVC 2014. But I don't use Windows or MSVC, so it's difficult for me to check.

It's fair to say that the cl driver mode stuff in libclang is fairly new and so some issues are to be expected.

I would recommend trying the upstream libclang trunk, as others have reported it to be more stable in this mode, though again YMMV :/

@joelfrederico
Copy link

@micbou It looks like it happens every time I try to complete.

@puremourning Hmmm... well... I may have the rest of the afternoon cut out for me then, that sounds involved.

@joelfrederico
Copy link

@micbou To be clear- when I use a STL vector and try to dot-complete by typing a dot in insert mode in Vim, nothing happens. When I press escape immediately after, I see a red status in vim: "ClangParseError: An AST deserialization error occurred while parsing the translation unit."

@puremourning
Copy link
Member

Building LLVM/clang isn't difficult, just time consuming. Don't forget to -DCMAKE_BUILD_TYPE=Release else it will build debug and be sloooooooow :)

@joelfrederico
Copy link

joelfrederico commented Feb 21, 2018

@puremourning Yeah, I think I'll give it a try. Even when it works, it's... weird. It was complaining about a signature mismatch for a method that definitely wasn't mismatched. The signature involved a vector. I restarted ycmd and got the AST error again. It may take awhile...

@bstaletic
Copy link
Collaborator Author

@puremourning I'm not sure if we should recognise clang-cl as C++ compiler. Since there is no clang-cl++, without specifying -x<language> it falls back to determining the language based on extension.

While the above patch works, it's not very efficient. Here's a better one:

diff --git a/ycmd/completers/cpp/flags.py b/ycmd/completers/cpp/flags.py
index 945a6af..0f1efad 100644
--- a/ycmd/completers/cpp/flags.py
+++ b/ycmd/completers/cpp/flags.py
@@ -244,9 +244,16 @@ def _ExtractFlagsList( flags_for_file_output ):
 def _ShouldAllowWinStyleFlags( flags ):
   enable_windows_style_flags = False
   if OnWindows():
-    for flag in flags:
+    # clang-cl implies --driver-mode=cl
+    enable_windows_style_flags = 'clang-cl' in flags[ 0 ]
+    # Iterate in reverse because we only care
+    # about the last occurance of --driver-mode flag.
+    # Even with clang-cl, if the last --driver-mode is 'gcc'
+    # we shouldn't enable win flags.
+    for flag in reversed( flags ):
       if flag.startswith( '--driver-mode' ):
-        enable_windows_style_flags = ( flag == '--driver-mode=cl' )
+        enable_windows_style_flags = flag == '--driver-mode=cl'
+        break
 
   return enable_windows_style_flags
 

@joelfrederico
Copy link

joelfrederico commented Feb 21, 2018

@bstaletic I'm not set up to do patches, but it looks like you should be able to skip checking the flags for --driver-mode=cl if you've detected we're using clang-cl already. There's no reason to allow somebody using clang-cl to disable windows-style flags, right? That's guaranteed to be broken, because clang-cl doesn't understand non-windows-stye flags. E.g.:

def _ShouldAllowWinStyleFlags( flags ):
  enable_windows_style_flags = False
  if OnWindows():
    if 'clang-cl' in flags[ 0 ]:
      return enable_windows_style_flags = True
    # Iterate in reverse because we only care
    # about the last occurance of --driver-mode flag.
    # Even with clang-cl, if the last --driver-mode is 'gcc'
    # we shouldn't enable win flags.
    for flag in reversed( flags ):
      if flag.startswith( '--driver-mode' ):
        enable_windows_style_flags = flag == '--driver-mode=cl'
        break
  return enable_windows_style_flags

@bstaletic
Copy link
Collaborator Author

bstaletic commented Feb 21, 2018

@joelfrederico clang-cl does understand and allow non Windows flags. What's even more annoying, you can do clang-cl --driver-mode=gcc and disable MSVC flags, essentially making it equivalent to clang.

@joelfrederico
Copy link

@bstaletic Really?!? Wow... I was going off of the clang-cl help, I guess that's not documented there.

@joelfrederico
Copy link

What's even more annoying, you can do clang-cl --driver-mode=cl and disable MSVC flags, essentially making it equivalent to clang.

Wait... if that's true, don't you want to reverse the sense of the test for the flags if clang-cl was found? So that enable_windows_style_flags is false if clang-cl is found and --driver-mode=cl was found?

@bstaletic
Copy link
Collaborator Author

Not really. Note that you can still do clang-cl foo.cpp /I include_dir --driver-mode=cl where the last flag is a no-op.
My patch does the following

  • First checks for clang-cl.
  • Then searches for the last flag starting with --driver-mode=.
    • If found and it equals --driver-mode=cl we are certain we are in CL enabled mode.
    • If found and it doesn't equal --driver-mode=cl we are certain we are not in CL enabled mode.
    • If not found it returns 'clang-cl' in flags[ 0 ] where flags[ 0 ] is actually the compiler name.

That matches with the clang behaviour.

@joelfrederico
Copy link

@bstaletic Do you have a link to the clang behaviour? It seems to me that your example breaks my understanding of flags.

At the very least, there's a logical inconsistency in your last post:

Note that you can still do clang-cl foo.cpp /I include_dir --driver-mode=cl where the last flag is a no-op.
My patch does the following

  • First checks for clang-cl.

Your example finds clang-cl.

  • Then searches for the last flag starting with --driver-mode=.

Your example has this flag.

  • If found and it equals --driver-mode=cl we are certain we are in CL enabled mode.

This condition is met. But you said earlier that the last flag is a no-op. So we aren't in CL-enabled mode, but your algorithm thinks we are.

  • If found and it doesn't equal --driver-mode=cl we are certain we are not in CL enabled mode.
  • If not found it returns 'clang-cl' in flags[ 0 ] where flags[ 0 ] is actually the compiler name.

These two lines aren't reached.

I mean, if you don't want to handle this edge-case, that's okay. I'm just a bit confused because this doesn't match the clang behavior you just described?

@bstaletic
Copy link
Collaborator Author

Do you have a link to the clang behaviour?

No, I have a very simple test case which I'm relying on. main(){} in a.c and playing with clang and clang-cl, watching out for errors.

You didn't understand what I meant by no-op. If you are using clang-cl it implies --driver-mode=cl, so by "no-op" I meant there's no reason to enable it again.

So we aren't in CL-enabled mode

This is where you are wrong. In the example above, MSVC flags are enabled, first with clang-cl, then with --driver-mode=cl.

@joelfrederico
Copy link

joelfrederico commented Feb 21, 2018

@bstaletic Ah but you said earlier:

What's even more annoying, you can do clang-cl --driver-mode=cl and disable MSVC flags, essentially making it equivalent to clang.

But that's not the same as:

If you are using clang-cl it implies --driver-mode=cl, so by "no-op" I meant there's no reason to enable it again.

Which one of those is true?? Or maybe I'm missing a subtlety here??

@bstaletic
Copy link
Collaborator Author

Heh, in the first example I was thinking of clang-cl --driver-mode=gcc. This would make it the same as clang. I made a quick edit and hoped no one would notice.

clang-cl --driver-mode=cl does just enable MSVC flags twice. So the second statement is true.

@joelfrederico
Copy link

@bstaletic Okay, then everything makes perfect sense! That initial comment threw me off. I'm on board now!

@joelfrederico
Copy link

joelfrederico commented Feb 21, 2018

@puremourning Okay, so- I built libclang.dll. The documentation regarding linking against an externally-built libclang.dll for Windows seems to be wrong:

  • You need to specify the path to llvm's root. That's the PATH_TO_LLVM_ROOT CMake variable.
  • Building libclang
    • This doesn't create the headers in ${PATH_TO_LLVM_ROOT}/include
    • You have to build the install target
    • It might be wise to set the install prefix in CMake so you don't overwrite a previous Clang installation

I'm fighting with LNK2001 errors. It seems like the final linking step of ycm_core isn't linking properly against libclang, or libclang didn't properly export symbols it needs.

Edit: I compiled libclang.dll as a 32-bit binary, I needed a 64-bit binary. I missed the warning that got buried. Maybe that should be an error, but it's something I should've noticed a lot sooner.

@puremourning
Copy link
Member

I'm sorry I have no experience of doing this on Windows, but my general approach is:

  • clone llvm/clang/etc. in some dir ($CLANG_SOURCE)
  • build llvm/clang into some other dir ($CLANG_BUILD)
  • install llvm/clang into a final dir ($CLANG_ROOT)
  • build YCM with env EXTRA_CMAKE_ARGS=-DPATH_TO_LLVM_ROOT=$CLANG_ROOT ./install.py --all --system-libclang

I realise that isn't an obvious way from the docs, but ultimately it boils down to using -DPATH_TO_LLVM_ROOT=$CLANG_ROOT when running ycmd's cmake. the --system-libclang just tells install.py to not try and find a libclang to use.

hope that helps.

@joelfrederico
Copy link

@puremourning I got it compiled, but I still get that AST deserialization error. I'm sort of ignoring it. The flags look fine to me and they work in some places, so this is a different error from flags (what's going on in this thread, right)? The right thing to do would be to figure out what my actual problem is (not Windows vs gcc flags) and open a separate issue, yes?

zzbot added a commit that referenced this pull request Feb 22, 2018
[READY] Add clang-cl support

According to #789 (comment), #789 didn't include support for `clang-cl` without `--driver-mode=cl` specified.

I did not add `clang-cl` to the C++ regex. Since there is no `clang-cl++` I think we should not touch language flags and let clang decide if it's C or C++.

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/valloric/ycmd/936)
<!-- Reviewable:end -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants