@ -61,6 +61,8 @@
# include <google/protobuf/stubs/strutil.h>
# include <google/protobuf/stubs/substitute.h>
# include <google/protobuf/stubs/map-util.h>
# include <google/protobuf/stubs/stl_util-inl.h>
# include <google/protobuf/stubs/hash.h>
namespace google {
@ -132,6 +134,45 @@ void SetFdToBinaryMode(int fd) {
// (Text and binary are the same on non-Windows platforms.)
}
void AddTrailingSlash ( string * path ) {
if ( ! path - > empty ( ) & & path - > at ( path - > size ( ) - 1 ) ! = ' / ' ) {
path - > push_back ( ' / ' ) ;
}
}
bool VerifyDirectoryExists ( const string & path ) {
if ( path . empty ( ) ) return true ;
if ( access ( path . c_str ( ) , W_OK ) = = - 1 ) {
cerr < < path < < " : " < < strerror ( errno ) < < endl ;
return false ;
} else {
return true ;
}
}
// Try to create the parent directory of the given file, creating the parent's
// parent if necessary, and so on. The full file name is actually
// (prefix + filename), but we assume |prefix| already exists and only create
// directories listed in |filename|.
void TryCreateParentDirectory ( const string & prefix , const string & filename ) {
// Recursively create parent directories to the output file.
vector < string > parts ;
SplitStringUsing ( filename , " / " , & parts ) ;
string path_so_far = prefix ;
for ( int i = 0 ; i < parts . size ( ) - 1 ; i + + ) {
path_so_far + = parts [ i ] ;
if ( mkdir ( path_so_far . c_str ( ) , 0777 ) ! = 0 ) {
if ( errno ! = EEXIST ) {
cerr < < filename < < " : while trying to create directory "
< < path_so_far < < " : " < < strerror ( errno ) < < endl ;
return ;
}
}
path_so_far + = ' / ' ;
}
}
} // namespace
// A MultiFileErrorCollector that prints errors to stderr.
@ -176,15 +217,12 @@ class CommandLineInterface::ErrorPrinter : public MultiFileErrorCollector,
// -------------------------------------------------------------------
// An OutputDirectory implementation that writes to disk.
class CommandLineInterface : : Disk OutputDirectory : public OutputDirectory {
class CommandLineInterface : : Memory OutputDirectory : public OutputDirectory {
public :
DiskOutputDirectory ( const string & root ) ;
~ Disk OutputDirectory( ) ;
MemoryOutputDirectory ( ) ;
~ Memory OutputDirectory( ) ;
bool VerifyExistence ( ) ;
inline bool had_error ( ) { return had_error_ ; }
inline void set_had_error ( bool value ) { had_error_ = value ; }
bool WriteAllToDisk ( ) ;
// implements OutputDirectory --------------------------------------
io : : ZeroCopyOutputStream * Open ( const string & filename ) ;
@ -192,362 +230,264 @@ class CommandLineInterface::DiskOutputDirectory : public OutputDirectory {
const string & filename , const string & insertion_point ) ;
private :
string root_ ;
friend class MemoryOutputStream ;
hash_map < string , string * > files_ ;
bool had_error_ ;
} ;
// A FileOutputStream that checks for errors in the destructor and reports
// them. We extend FileOutputStream via wrapping rather than inheritance
// for two reasons:
// 1) Implementation inheritance is evil.
// 2) We need to close the file descriptor *after* the FileOutputStream's
// destructor is run to make sure it flushes the file contents.
class CommandLineInterface : : ErrorReportingFileOutput
: public io : : ZeroCopyOutputStream {
// OutputDirectory that just adds some prefix to every file name.
class CommandLineInterface : : SubOutputDirectory : public OutputDirectory {
public :
ErrorReportingFileOutput ( int file_descriptor ,
const string & filename ,
DiskOutputDirectory * directory ) ;
~ ErrorReportingFileOutput ( ) ;
SubOutputDirectory ( OutputDirectory * parent , const string & prefix )
: parent_ ( parent ) , prefix_ ( prefix ) { }
~ SubOutputDirectory ( ) { }
// implements ZeroCopyOutputStream ---------------------------------
bool Next ( void * * data , int * size ) { return file_stream_ - > Next ( data , size ) ; }
void BackUp ( int count ) { file_stream_ - > BackUp ( count ) ; }
int64 ByteCount ( ) const { return file_stream_ - > ByteCount ( ) ; }
// implements OutputDirectory --------------------------------------
io : : ZeroCopyOutputStream * Open ( const string & filename ) {
// TODO(kenton): This is not the cleanest place to deal with creation of
// parent directories, but it does the right thing given the way this
// class is used, and this class is private to this file anyway, so it's
// probably not worth fixing for now.
TryCreateParentDirectory ( prefix_ , filename ) ;
return parent_ - > Open ( prefix_ + filename ) ;
}
io : : ZeroCopyOutputStream * OpenForInsert (
const string & filename , const string & insertion_point ) {
return parent_ - > OpenForInsert ( prefix_ + filename , insertion_point ) ;
}
private :
scoped_ptr < io : : FileOutputStream > file_stream_ ;
string filename_ ;
DiskOutputDirectory * directory_ ;
OutputDirectory * parent_ ;
string prefix_ ;
} ;
// Kind of like ErrorReportingFileOutput, but used when inserting
// (OutputDirectory::OpenForInsert()). In this case, we are writing to a
// temporary file, since we must copy data from the original. We copy the
// data up to the insertion point in the constructor, and the remainder in the
// destructor. We then replace the original file with the temporary, also in
// the destructor.
class CommandLineInterface : : InsertionOutputStream
class CommandLineInterface : : MemoryOutputStream
: public io : : ZeroCopyOutputStream {
public :
InsertionOutputStream (
const string & filename ,
const string & temp_filename ,
const string & insertion_point ,
int original_file_descriptor , // Takes ownership.
int temp_file_descriptor , // Takes ownership.
DiskOutputDirectory * directory ) ; // Does not take ownership.
~ InsertionOutputStream ( ) ;
MemoryOutputStream ( MemoryOutputDirectory * directory , const string & filename ) ;
MemoryOutputStream ( MemoryOutputDirectory * directory , const string & filename ,
const string & insertion_point ) ;
virtual ~ MemoryOutputStream ( ) ;
// implements ZeroCopyOutputStream ---------------------------------
bool Next ( void * * data , int * size ) { return temp_file _- > Next ( data , size ) ; }
void BackUp ( int count ) { temp_file _- > BackUp ( count ) ; }
int64 ByteCount ( ) const { return temp_file _- > ByteCount ( ) ; }
virtual bool Next ( void * * data , int * size ) { return inner_ - > Next ( data , size ) ; }
virtual void BackUp ( int count ) { inner_ - > BackUp ( count ) ; }
virtual int64 ByteCount ( ) const { return inner _- > ByteCount ( ) ; }
private :
scoped_ptr < io : : FileInputStream > original_file_ ;
scoped_ptr < io : : FileOutputStream > temp_file_ ;
// Where to insert the string when it's done.
MemoryOutputDirectory * directory_ ;
string filename_ ;
string temp_filename_ ;
DiskOutputDirectory * directory_ ;
string insertion_point_ ;
// The contents of the line containing the insertion point.
string magic_line_ ;
// The string we're building.
string data_ ;
// StringOutputStream writing to data_.
scoped_ptr < io : : StringOutputStream > inner_ ;
} ;
// -------------------------------------------------------------------
CommandLineInterface : : DiskOutputDirectory : : DiskOutputDirectory (
const string & root )
: root_ ( root ) , had_error_ ( false ) {
// Add a '/' to the end if it doesn't already have one. But don't add a
// '/' to an empty string since this probably means the current directory.
if ( ! root_ . empty ( ) & & root [ root_ . size ( ) - 1 ] ! = ' / ' ) {
root_ + = ' / ' ;
}
}
CommandLineInterface : : MemoryOutputDirectory : : MemoryOutputDirectory ( )
: had_error_ ( false ) { }
CommandLineInterface : : DiskOutputDirectory : : ~ DiskOutputDirectory ( ) {
CommandLineInterface : : MemoryOutputDirectory : : ~ MemoryOutputDirectory ( ) {
STLDeleteValues ( & files_ ) ;
}
bool CommandLineInterface : : DiskOutputDirectory : : VerifyExistence ( ) {
if ( ! root_ . empty ( ) ) {
// Make sure the directory exists. If it isn't a directory, this will fail
// because we added a '/' to the end of the name in the constructor.
if ( access ( root_ . c_str ( ) , W_OK ) = = - 1 ) {
cerr < < root_ < < " : " < < strerror ( errno ) < < endl ;
return false ;
}
bool CommandLineInterface : : MemoryOutputDirectory : : WriteAllToDisk ( ) {
if ( had_error_ ) {
return false ;
}
return true ;
}
// -------------------------------------------------------------------
for ( hash_map < string , string * > : : const_iterator iter = files_ . begin ( ) ;
iter ! = files_ . end ( ) ; + + iter ) {
const string & filename = iter - > first ;
const char * data = iter - > second - > data ( ) ;
int size = iter - > second - > size ( ) ;
// Create the output file.
int file_descriptor ;
do {
file_descriptor =
open ( filename . c_str ( ) , O_WRONLY | O_CREAT | O_TRUNC | O_BINARY , 0666 ) ;
} while ( file_descriptor < 0 & & errno = = EINTR ) ;
if ( file_descriptor < 0 ) {
int error = errno ;
cerr < < filename < < " : " < < strerror ( error ) ;
return false ;
}
io : : ZeroCopyOutputStream * CommandLineInterface : : DiskOutputDirectory : : Open (
const string & filename ) {
// Recursively create parent directories to the output file.
vector < string > parts ;
SplitStringUsing ( filename , " / " , & parts ) ;
string path_so_far = root_ ;
for ( int i = 0 ; i < parts . size ( ) - 1 ; i + + ) {
path_so_far + = parts [ i ] ;
if ( mkdir ( path_so_far . c_str ( ) , 0777 ) ! = 0 ) {
if ( errno ! = EEXIST ) {
cerr < < filename < < " : while trying to create directory "
< < path_so_far < < " : " < < strerror ( errno ) < < endl ;
had_error_ = true ;
// Return a dummy stream.
return new io : : ArrayOutputStream ( NULL , 0 ) ;
// Write the file.
while ( size > 0 ) {
int write_result ;
do {
write_result = write ( file_descriptor , data , size ) ;
} while ( write_result < 0 & & errno = = EINTR ) ;
if ( write_result < = 0 ) {
// Write error.
// FIXME(kenton): According to the man page, if write() returns zero,
// there was no error; write() simply did not write anything. It's
// unclear under what circumstances this might happen, but presumably
// errno won't be set in this case. I am confused as to how such an
// event should be handled. For now I'm treating it as an error,
// since retrying seems like it could lead to an infinite loop. I
// suspect this never actually happens anyway.
if ( write_result < 0 ) {
int error = errno ;
cerr < < filename < < " : write: " < < strerror ( error ) ;
} else {
cerr < < filename < < " : write() returned zero? " < < endl ;
}
return false ;
}
data + = write_result ;
size - = write_result ;
}
path_so_far + = ' / ' ;
}
// Create the output file.
int file_descriptor ;
do {
file_descriptor =
open ( ( root_ + filename ) . c_str ( ) ,
O_WRONLY | O_CREAT | O_TRUNC | O_BINARY , 0666 ) ;
} while ( file_descriptor < 0 & & errno = = EINTR ) ;
if ( file_descriptor < 0 ) {
// Failed to open.
cerr < < filename < < " : " < < strerror ( errno ) < < endl ;
had_error_ = true ;
// Return a dummy stream.
return new io : : ArrayOutputStream ( NULL , 0 ) ;
if ( close ( file_descriptor ) ! = 0 ) {
int error = errno ;
cerr < < filename < < " : close: " < < strerror ( error ) ;
return false ;
}
}
return new ErrorReportingFileOutput ( file_descriptor , filename , this ) ;
return true ;
}
CommandLineInterface : : ErrorReportingFileOutput : : ErrorReportingFileOutput (
int file_descriptor ,
const string & filename ,
DiskOutputDirectory * directory )
: file_stream_ ( new io : : FileOutputStream ( file_descriptor ) ) ,
filename_ ( filename ) ,
directory_ ( directory ) { }
CommandLineInterface : : ErrorReportingFileOutput : : ~ ErrorReportingFileOutput ( ) {
// Check if we had any errors while writing.
if ( file_stream_ - > GetErrno ( ) ! = 0 ) {
cerr < < filename_ < < " : " < < strerror ( file_stream_ - > GetErrno ( ) ) < < endl ;
directory_ - > set_had_error ( true ) ;
}
// Close the file stream.
if ( ! file_stream_ - > Close ( ) ) {
cerr < < filename_ < < " : " < < strerror ( file_stream_ - > GetErrno ( ) ) < < endl ;
directory_ - > set_had_error ( true ) ;
}
io : : ZeroCopyOutputStream * CommandLineInterface : : MemoryOutputDirectory : : Open (
const string & filename ) {
return new MemoryOutputStream ( this , filename ) ;
}
// -------------------------------------------------------------------
io : : ZeroCopyOutputStream *
CommandLineInterface : : Disk OutputDirectory: : OpenForInsert (
CommandLineInterface : : MemoryOutputDirectory : : OpenForInsert (
const string & filename , const string & insertion_point ) {
string path = root_ + filename ;
// Put the temp file in the same directory so that we can simply rename() it
// into place later.
string temp_path = path + " .protoc_temp " ;
// Open the original file.
int original_file ;
do {
original_file = open ( path . c_str ( ) , O_RDONLY | O_BINARY ) ;
} while ( original_file < 0 & & errno = = EINTR ) ;
return new MemoryOutputStream ( this , filename , insertion_point ) ;
}
if ( original_file < 0 ) {
// Failed to open.
cerr < < path < < " : " < < strerror ( errno ) < < endl ;
had_error_ = true ;
// Return a dummy stream.
return new io : : ArrayOutputStream ( NULL , 0 ) ;
}
// -------------------------------------------------------------------
// Create the temp file.
int temp_file ;
do {
temp_file =
open ( temp_path . c_str ( ) ,
O_WRONLY | O_CREAT | O_TRUNC | O_BINARY , 0666 ) ;
} while ( temp_file < 0 & & errno = = EINTR ) ;
if ( temp_file < 0 ) {
// Failed to open.
cerr < < temp_path < < " : " < < strerror ( errno ) < < endl ;
had_error_ = true ;
close ( original_file ) ;
// Return a dummy stream.
return new io : : ArrayOutputStream ( NULL , 0 ) ;
}
CommandLineInterface : : MemoryOutputStream : : MemoryOutputStream (
MemoryOutputDirectory * directory , const string & filename )
: directory_ ( directory ) ,
filename_ ( filename ) ,
inner_ ( new io : : StringOutputStream ( & data_ ) ) {
}
return new InsertionOutputStream (
path , temp_path , insertion_point , original_file , temp_file , this ) ;
CommandLineInterface : : MemoryOutputStream : : MemoryOutputStream (
MemoryOutputDirectory * directory , const string & filename ,
const string & insertion_point )
: directory_ ( directory ) ,
filename_ ( filename ) ,
insertion_point_ ( insertion_point ) ,
inner_ ( new io : : StringOutputStream ( & data_ ) ) {
}
namespace {
CommandLineInterface : : MemoryOutputStream : : ~ MemoryOutputStream ( ) {
// Make sure all data has been written.
inner_ . reset ( ) ;
// Helper for reading lines from a ZeroCopyInputStream.
// TODO(kenton): Put somewhere reusable?
class LineReader {
public :
LineReader ( io : : ZeroCopyInputStream * input )
: input_ ( input ) , buffer_ ( NULL ) , size_ ( 0 ) { }
// Insert into the directory.
string * * map_slot = & directory_ - > files_ [ filename_ ] ;
~ LineReader ( ) {
if ( size_ > 0 ) {
input_ - > BackUp ( size_ ) ;
if ( insertion_point_ . empty ( ) ) {
// This was just a regular Open().
if ( * map_slot ! = NULL ) {
cerr < < filename_ < < " : Tried to write the same file twice. " < < endl ;
directory_ - > had_error_ = true ;
return ;
}
}
bool ReadLine ( string * line ) {
line - > clear ( ) ;
while ( true ) {
for ( int i = 0 ; i < size_ ; i + + ) {
if ( buffer_ [ i ] = = ' \n ' ) {
line - > append ( buffer_ , i + 1 ) ;
buffer_ + = i + 1 ;
size_ - = i + 1 ;
return true ;
}
}
line - > append ( buffer_ , size_ ) ;
const void * void_buffer ;
if ( ! input_ - > Next ( & void_buffer , & size_ ) ) {
buffer_ = NULL ;
size_ = 0 ;
return false ;
}
* map_slot = new string ;
( * map_slot ) - > swap ( data_ ) ;
} else {
// This was an OpenForInsert().
buffer_ = reinterpret_cast < const char * > ( void_buffer ) ;
// If the data doens't end with a clean line break, add one.
if ( ! data_ . empty ( ) & & data_ [ data_ . size ( ) - 1 ] ! = ' \n ' ) {
data_ . push_back ( ' \n ' ) ;
}
}
private :
io : : ZeroCopyInputStream * input_ ;
const char * buffer_ ;
int size_ ;
} ;
} // namespace
CommandLineInterface : : InsertionOutputStream : : InsertionOutputStream (
const string & filename ,
const string & temp_filename ,
const string & insertion_point ,
int original_file_descriptor ,
int temp_file_descriptor ,
DiskOutputDirectory * directory )
: original_file_ ( new io : : FileInputStream ( original_file_descriptor ) ) ,
temp_file_ ( new io : : FileOutputStream ( temp_file_descriptor ) ) ,
filename_ ( filename ) ,
temp_filename_ ( temp_filename ) ,
directory_ ( directory ) {
string magic_string = strings : : Substitute (
" @@protoc_insertion_point($0) " , insertion_point ) ;
LineReader reader ( original_file_ . get ( ) ) ;
io : : Printer writer ( temp_file_ . get ( ) , ' $ ' ) ;
string line ;
while ( true ) {
if ( ! reader . ReadLine ( & line ) ) {
int error = temp_file_ - > GetErrno ( ) ;
if ( error = = 0 ) {
cerr < < filename < < " : Insertion point not found: "
< < insertion_point < < endl ;
} else {
cerr < < filename < < " : " < < strerror ( error ) < < endl ;
}
original_file_ - > Close ( ) ;
original_file_ . reset ( ) ;
// Will finish handling error in the destructor.
break ;
// Find the file we are going to insert into.
if ( * map_slot = = NULL ) {
cerr < < filename_ < < " : Tried to insert into file that doesn't exist. "
< < endl ;
directory_ - > had_error_ = true ;
return ;
}
if ( line . find ( magic_string ) ! = string : : npos ) {
// Found the magic line. Since we want to insert before it, save it for
// later.
magic_line_ = line ;
break ;
string * target = * map_slot ;
// Find the insertion point.
string magic_string = strings : : Substitute (
" @@protoc_insertion_point($0) " , insertion_point_ ) ;
string : : size_type pos = target - > find ( magic_string ) ;
if ( pos = = string : : npos ) {
cerr < < filename_ < < " : insertion point \" " < < insertion_point_
< < " \" not found. " < < endl ;
directory_ - > had_error_ = true ;
return ;
}
writer . PrintRaw ( line ) ;
}
}
CommandLineInterface : : InsertionOutputStream : : ~ InsertionOutputStream ( ) {
// C-style error handling is teh best.
bool had_error = false ;
if ( original_file_ = = NULL ) {
// We had an error in the constructor.
had_error = true ;
} else {
// Use CodedOutputStream for convenience, so we don't have to deal with
// copying buffers ourselves.
io : : CodedOutputStream out ( temp_file_ . get ( ) ) ;
out . WriteRaw ( magic_line_ . data ( ) , magic_line_ . size ( ) ) ;
// Write the rest of the original file.
const void * buffer ;
int size ;
while ( original_file_ - > Next ( & buffer , & size ) ) {
out . WriteRaw ( buffer , size ) ;
// Seek backwards to the beginning of the line, which is where we will
// insert the data. Note that this has the effect of pushing the insertion
// point down, so the data is inserted before it. This is intentional
// because it means that multiple insertions at the same point will end
// up in the expected order in the final output.
pos = target - > find_last_of ( ' \n ' , pos ) ;
if ( pos = = string : : npos ) {
// Insertion point is on the first line.
pos = 0 ;
} else {
// Advance to character after '\n'.
+ + pos ;
}
// Close the original file.
if ( ! original_file_ - > Close ( ) ) {
cerr < < filename_ < < " : " < < strerror ( original_file_ - > GetErrno ( ) ) < < endl ;
had_error = true ;
}
}
// Extract indent.
string indent_ ( * target , pos , target - > find_first_not_of ( " \t " , pos ) - pos ) ;
// Check if we had any errors while writing.
if ( temp_file_ - > GetErrno ( ) ! = 0 ) {
cerr < < filename_ < < " : " < < strerror ( temp_file_ - > GetErrno ( ) ) < < endl ;
had_error = true ;
}
if ( indent_ . empty ( ) ) {
// No indent. This makes things easier.
target - > insert ( pos , data_ ) ;
} else {
// Calculate how much space we need.
int indent_size = 0 ;
for ( int i = 0 ; i < data_ . size ( ) ; i + + ) {
if ( data_ [ i ] = = ' \n ' ) indent_size + = indent_ . size ( ) ;
}
// Close the temp file.
if ( ! temp_file_ - > Close ( ) ) {
cerr < < filename_ < < " : " < < strerror ( temp_file_ - > GetErrno ( ) ) < < endl ;
had_error = true ;
}
// Make a hole for it.
target - > insert ( pos , data_ . size ( ) + indent_size , ' \0 ' ) ;
// Now copy in the data.
string : : size_type data_pos = 0 ;
char * target_ptr = string_as_array ( target ) + pos ;
while ( data_pos < data_ . size ( ) ) {
// Copy indent.
memcpy ( target_ptr , indent_ . data ( ) , indent_ . size ( ) ) ;
target_ptr + = indent_ . size ( ) ;
// Copy line from data_.
// We already guaranteed that data_ ends with a newline (above), so this
// search can't fail.
string : : size_type line_length =
data_ . find_first_of ( ' \n ' , data_pos ) + 1 - data_pos ;
memcpy ( target_ptr , data_ . data ( ) + data_pos , line_length ) ;
target_ptr + = line_length ;
data_pos + = line_length ;
}
// If everything was successful, overwrite the original file with the temp
// file.
if ( ! had_error ) {
# ifdef _WIN32
// rename() on Windows fails if the file exists.
if ( ! MoveFileEx ( temp_filename_ . c_str ( ) , filename_ . c_str ( ) ,
MOVEFILE_REPLACE_EXISTING ) ) {
cerr < < filename_ < < " : MoveFileEx: "
< < Subprocess : : Win32ErrorMessage ( GetLastError ( ) ) < < endl ;
}
# else // _WIN32
if ( rename ( temp_filename_ . c_str ( ) , filename_ . c_str ( ) ) < 0 ) {
cerr < < filename_ < < " : rename: " < < strerror ( errno ) < < endl ;
had_error = true ;
GOOGLE_CHECK_EQ ( target_ptr ,
string_as_array ( target ) + pos + data_ . size ( ) + indent_size ) ;
}
# endif // !_WIN32
}
if ( had_error ) {
// We had some sort of error so let's try to delete the temp file.
remove ( temp_filename_ . c_str ( ) ) ;
directory_ - > set_had_error ( true ) ;
}
}
@ -613,14 +553,20 @@ int CommandLineInterface::Run(int argc, const char* const argv[]) {
}
// Generate output.
MemoryOutputDirectory output_directory ;
if ( mode_ = = MODE_COMPILE ) {
for ( int i = 0 ; i < output_directives_ . size ( ) ; i + + ) {
if ( ! GenerateOutput ( parsed_files , output_directives_ [ i ] ) ) {
if ( ! GenerateOutput ( parsed_files , output_directives_ [ i ] ,
& output_directory ) ) {
return 1 ;
}
}
}
if ( ! output_directory . WriteAllToDisk ( ) ) {
return 1 ;
}
if ( ! descriptor_set_name_ . empty ( ) ) {
if ( ! WriteDescriptorSet ( parsed_files ) ) {
return 1 ;
@ -1062,14 +1008,15 @@ void CommandLineInterface::PrintHelpText() {
bool CommandLineInterface : : GenerateOutput (
const vector < const FileDescriptor * > & parsed_files ,
const OutputDirective & output_directive ) {
// Create the output directory.
DiskOutputDirectory output_directory ( output_directive . output_location ) ;
if ( ! output_directory . VerifyExistence ( ) ) {
const OutputDirective & output_directive ,
OutputDirectory * parent_output_directory ) {
// Set up the OutputDirectory.
string path = output_directive . output_location ;
AddTrailingSlash ( & path ) ;
if ( ! VerifyDirectoryExists ( path ) ) {
return false ;
}
// Opened successfully. Write it.
SubOutputDirectory output_directory ( parent_output_directory , path ) ;
// Call the generator.
string error ;
@ -1103,11 +1050,6 @@ bool CommandLineInterface::GenerateOutput(
}
}
// Check for write errors.
if ( output_directory . had_error ( ) ) {
return false ;
}
return true ;
}